tree arg0, tree arg1, tree arg2)
{
enum internal_fn ifn = IFN_LAST;
- /* The code of the expression corresponding to the type-generic
- built-in, or ERROR_MARK for the type-specific ones. */
+ /* The code of the expression corresponding to the built-in. */
enum tree_code opcode = ERROR_MARK;
bool ovf_only = false;
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_ADD_OVERFLOW:
- opcode = PLUS_EXPR;
- /* FALLTHRU */
case BUILT_IN_SADD_OVERFLOW:
case BUILT_IN_SADDL_OVERFLOW:
case BUILT_IN_SADDLL_OVERFLOW:
case BUILT_IN_UADD_OVERFLOW:
case BUILT_IN_UADDL_OVERFLOW:
case BUILT_IN_UADDLL_OVERFLOW:
+ opcode = PLUS_EXPR;
ifn = IFN_ADD_OVERFLOW;
break;
case BUILT_IN_SUB_OVERFLOW_P:
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_SUB_OVERFLOW:
- opcode = MINUS_EXPR;
- /* FALLTHRU */
case BUILT_IN_SSUB_OVERFLOW:
case BUILT_IN_SSUBL_OVERFLOW:
case BUILT_IN_SSUBLL_OVERFLOW:
case BUILT_IN_USUB_OVERFLOW:
case BUILT_IN_USUBL_OVERFLOW:
case BUILT_IN_USUBLL_OVERFLOW:
+ opcode = MINUS_EXPR;
ifn = IFN_SUB_OVERFLOW;
break;
case BUILT_IN_MUL_OVERFLOW_P:
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_MUL_OVERFLOW:
- opcode = MULT_EXPR;
- /* FALLTHRU */
case BUILT_IN_SMUL_OVERFLOW:
case BUILT_IN_SMULL_OVERFLOW:
case BUILT_IN_SMULLL_OVERFLOW:
case BUILT_IN_UMUL_OVERFLOW:
case BUILT_IN_UMULL_OVERFLOW:
case BUILT_IN_UMULLL_OVERFLOW:
+ opcode = MULT_EXPR;
ifn = IFN_MUL_OVERFLOW;
break;
default:
? boolean_true_node : boolean_false_node,
arg2);
- tree ctype = build_complex_type (type);
- tree call = build_call_expr_internal_loc (loc, ifn, ctype,
- 2, arg0, arg1);
- tree tgt = save_expr (call);
- tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
- tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
- ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+ tree intres, ovfres;
+ if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ intres = fold_binary_loc (loc, opcode, type,
+ fold_convert_loc (loc, type, arg0),
+ fold_convert_loc (loc, type, arg1));
+ if (TREE_OVERFLOW (intres))
+ intres = drop_tree_overflow (intres);
+ ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
+ ? boolean_true_node : boolean_false_node);
+ }
+ else
+ {
+ tree ctype = build_complex_type (type);
+ tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
+ arg0, arg1);
+ tree tgt = save_expr (call);
+ intres = build1_loc (loc, REALPART_EXPR, type, tgt);
+ ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
+ ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+ }
if (ovf_only)
return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
struct GTY((for_user)) constexpr_fundef {
tree decl;
tree body;
+ tree parms;
+ tree result;
};
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
static constexpr_fundef *
retrieve_constexpr_fundef (tree fun)
{
- constexpr_fundef fundef = { NULL, NULL };
if (constexpr_fundef_table == NULL)
return NULL;
- fundef.decl = fun;
+ constexpr_fundef fundef = { fun, NULL, NULL, NULL };
return constexpr_fundef_table->find (&fundef);
}
= hash_table<constexpr_fundef_hasher>::create_ggc (101);
entry.decl = fun;
- entry.body = body;
+ tree saved_fn = current_function_decl;
+ bool clear_ctx = false;
+ current_function_decl = fun;
+ if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+ {
+ clear_ctx = true;
+ DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+ }
+ entry.body = copy_fn (fun, entry.parms, entry.result);
+ current_function_decl = saved_fn;
slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+ if (clear_ctx)
+ DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
gcc_assert (*slot == NULL);
*slot = ggc_alloc<constexpr_fundef> ();
is parms, TYPE is result. */
static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
{
maybe_initialize_fundef_copies_table ();
tree copy;
bool existed;
- tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+ tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
if (!existed)
{
/* There is no cached function available, or in use. We can use
the function directly. That the slot is now created records
that this function is now in use. */
- copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
- TREE_TYPE (copy) = DECL_RESULT (fun);
+ copy = build_tree_list (fundef->body, fundef->parms);
+ TREE_TYPE (copy) = fundef->result;
}
else if (*slot == NULL_TREE)
{
/* We've already used the function itself, so make a copy. */
copy = build_tree_list (NULL, NULL);
- TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+ tree saved_body = DECL_SAVED_TREE (fundef->decl);
+ tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+ tree saved_result = DECL_RESULT (fundef->decl);
+ tree saved_fn = current_function_decl;
+ DECL_SAVED_TREE (fundef->decl) = fundef->body;
+ DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+ DECL_RESULT (fundef->decl) = fundef->result;
+ current_function_decl = fundef->decl;
+ TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+ TREE_TYPE (copy));
+ current_function_decl = saved_fn;
+ DECL_RESULT (fundef->decl) = saved_result;
+ DECL_ARGUMENTS (fundef->decl) = saved_parms;
+ DECL_SAVED_TREE (fundef->decl) = saved_body;
}
else
{
bool save_ffbcp = force_folding_builtin_constant_p;
force_folding_builtin_constant_p = true;
+ tree save_cur_fn = current_function_decl;
+ /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION (). */
+ if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+ && ctx->call
+ && ctx->call->fundef)
+ current_function_decl = ctx->call->fundef->decl;
new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
CALL_EXPR_FN (t), nargs, args);
+ current_function_decl = save_cur_fn;
force_folding_builtin_constant_p = save_ffbcp;
if (new_call == NULL)
{
return t;
}
- if (!is_constant_expression (new_call))
+ if (!potential_constant_expression (new_call))
{
if (!*non_constant_p && !ctx->quiet)
error ("%q+E is not a constant expression", new_call);
{
const int nargs = call_expr_nargs (t);
tree fun = new_call->fundef->decl;
- tree parms = DECL_ARGUMENTS (fun);
+ tree parms = new_call->fundef->parms;
int i;
tree *p = &new_call->bindings;
for (i = 0; i < nargs; ++i)
x = ctx->object;
x = build_address (x);
}
+ if (TREE_ADDRESSABLE (type))
+ /* Undo convert_for_arg_passing work here. */
+ x = convert_from_reference (x);
arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
tree body, parms, res;
/* Reuse or create a new unshared copy of this function's body. */
- tree copy = get_fundef_copy (fun);
+ tree copy = get_fundef_copy (new_call.fundef);
body = TREE_PURPOSE (copy);
parms = TREE_VALUE (copy);
res = TREE_TYPE (copy);
VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
- return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
- lval,
- non_constant_p, overflow_p,
- jump_target);
- return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- lval,
- non_constant_p, overflow_p,
- jump_target);
+ val = TREE_OPERAND (t, 2);
+ else
+ val = TREE_OPERAND (t, 1);
+ if (TREE_CODE (t) == IF_STMT && !val)
+ val = void_node;
+ return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+ overflow_p, jump_target);
}
/* Subroutine of cxx_eval_constant_expression.
}
break;
+ case BREAK_STMT:
+ case CONTINUE_STMT:
+ /* These two are handled directly in cxx_eval_loop_expr by testing
+ breaks (jump_target) or continues (jump_target). */
+ break;
+
default:
gcc_unreachable ();
}
{
constexpr_ctx new_ctx = *ctx;
- tree body = TREE_OPERAND (t, 0);
+ tree body, cond = NULL_TREE, expr = NULL_TREE;
int count = 0;
+ switch (TREE_CODE (t))
+ {
+ case LOOP_EXPR:
+ body = LOOP_EXPR_BODY (t);
+ break;
+ case DO_STMT:
+ body = DO_BODY (t);
+ cond = DO_COND (t);
+ break;
+ case WHILE_STMT:
+ body = WHILE_BODY (t);
+ cond = WHILE_COND (t);
+ count = -1;
+ break;
+ case FOR_STMT:
+ if (FOR_INIT_STMT (t))
+ cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+ non_constant_p, overflow_p, jump_target);
+ if (*non_constant_p)
+ return NULL_TREE;
+ body = FOR_BODY (t);
+ cond = FOR_COND (t);
+ expr = FOR_EXPR (t);
+ count = -1;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ hash_set<tree> save_exprs;
+ new_ctx.save_exprs = &save_exprs;
do
{
- hash_set<tree> save_exprs;
- new_ctx.save_exprs = &save_exprs;
+ if (count != -1)
+ {
+ if (body)
+ cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (breaks (jump_target))
+ {
+ *jump_target = NULL_TREE;
+ break;
+ }
- cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
- non_constant_p, overflow_p, jump_target);
+ if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+ *jump_target = NULL_TREE;
+
+ if (expr)
+ cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ }
+
+ if (cond)
+ {
+ tree res
+ = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (res)
+ {
+ if (verify_constant (res, ctx->quiet, non_constant_p,
+ overflow_p))
+ break;
+ if (integer_zerop (res))
+ break;
+ }
+ else
+ gcc_assert (*jump_target);
+ }
/* Forget saved values of SAVE_EXPRs. */
for (hash_set<tree>::iterator iter = save_exprs.begin();
iter != save_exprs.end(); ++iter)
new_ctx.values->remove (*iter);
+
if (++count >= constexpr_loop_limit)
{
if (!ctx->quiet)
}
while (!returns (jump_target)
&& !breaks (jump_target)
- && !switches (jump_target)
+ && !continues (jump_target)
+ && (!switches (jump_target) || count == 0)
&& !*non_constant_p);
- if (breaks (jump_target))
- *jump_target = NULL_TREE;
+ /* Forget saved values of SAVE_EXPRs. */
+ for (hash_set<tree>::iterator iter = save_exprs.begin();
+ iter != save_exprs.end(); ++iter)
+ new_ctx.values->remove (*iter);
return NULL_TREE;
}
bool *non_constant_p, bool *overflow_p,
tree *jump_target)
{
- tree cond = SWITCH_COND (t);
+ tree cond
+ = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
cond = cxx_eval_constant_expression (ctx, cond, false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (cond);
*jump_target = cond;
- tree body = SWITCH_BODY (t);
+ tree body
+ = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
constexpr_ctx new_ctx = *ctx;
constexpr_switch_state css = css_default_not_seen;
new_ctx.css_state = &css;
case STATEMENT_LIST:
case LOOP_EXPR:
case COND_EXPR:
+ case IF_STMT:
+ case DO_STMT:
+ case WHILE_STMT:
+ case FOR_STMT:
break;
case LABEL_EXPR:
case CASE_LABEL_EXPR:
case DECL_EXPR:
{
r = DECL_EXPR_DECL (t);
+ if (TREE_CODE (r) == USING_DECL)
+ {
+ r = void_node;
+ break;
+ }
if (AGGREGATE_TYPE_P (TREE_TYPE (r))
|| VECTOR_TYPE_P (TREE_TYPE (r)))
{
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
lval,
non_constant_p, overflow_p);
+ /* FALLTHRU */
+ case BREAK_STMT:
+ case CONTINUE_STMT:
if (jump_target)
*jump_target = t;
else
jump_target);
break;
+ case CLEANUP_STMT:
+ r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+ /* Also evaluate the cleanup. */
+ cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+ non_constant_p, overflow_p,
+ jump_target);
+ break;
+
/* These differ from cxx_eval_unary_expression in that this doesn't
check for a constant operand or result; an address can be
constant without its operand being, and vice versa. */
break;
case SIZEOF_EXPR:
- r = fold_sizeof_expr (t);
- VERIFY_CONSTANT (r);
+ r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+ non_constant_p, overflow_p,
+ jump_target);
break;
case COMPOUND_EXPR:
break;
case COND_EXPR:
+ case IF_STMT:
if (jump_target && *jump_target)
{
tree orig_jump = *jump_target;
+ tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+ ? TREE_OPERAND (t, 1) : void_node);
/* When jumping to a label, the label might be either in the
then or else blocks, so process then block first in skipping
mode first, and if we are still in the skipping mode at its end,
process the else block too. */
- r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- lval, non_constant_p, overflow_p,
- jump_target);
+ r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+ overflow_p, jump_target);
/* It's possible that we found the label in the then block. But
it could have been followed by another jumping statement, e.g.
say we're looking for case 1:
in which case we need not go looking to the else block.
(goto is not allowed in a constexpr function.) */
if (*jump_target == orig_jump)
- r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
- lval, non_constant_p, overflow_p,
- jump_target);
+ {
+ arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+ ? TREE_OPERAND (t, 2) : void_node);
+ r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+ overflow_p, jump_target);
+ }
break;
}
r = cxx_eval_conditional_expression (ctx, t, lval,
break;
case LOOP_EXPR:
+ case DO_STMT:
+ case WHILE_STMT:
+ case FOR_STMT:
cxx_eval_loop_expr (ctx, t,
non_constant_p, overflow_p, jump_target);
break;
case SWITCH_EXPR:
+ case SWITCH_STMT:
cxx_eval_switch_expr (ctx, t,
non_constant_p, overflow_p, jump_target);
break;