c++: improve constexpr call caching [PR115639]
For the testcase from this PR, checking static_assert(0 == big_calc()); takes twice as much time as constexpr int ret = big_calc(); static_assert(0 == ret); ultimately because in the former, we first constant evaluate big_calc() with mce_unknown (as part of warning-dependent folding from cp_build_binary_op). We then constant evaluate it a second time, with mce_true, during finish_static_assert. The result of the first evaluation isn't reused because of the different mce_value, which in general can give a different result. But big_calc() here doesn't depend on mce_value at all (i.e. there's no if consteval or __builtin_is_constant_evaluated calls, nested or otherwise) so we should be able to reuse the result in such cases. Specifically if a constexpr call with mce_unknown succeeds, we can safely reuse the result during a subsequent mce_true or mce_false evaluation. This patch implements this by also caching a successful mce_unknown call result into the corresponding mce_true and mce_false slots, so that such a subsequent evaluation effectively reuses the mce_unknown result. To make it more convenient to access the cache slot for the same call with different mce_value, this patch gives each constexpr_call entry three result slots, one per mce_value, instead of having a distinct constexpr_call entry for each mce_value. And we can no longer use NULL_TREE to denote the call is in progress; instead use unknown_type_node. After this patch compile time for the above two fragments is the same. PR c++/115639 gcc/cp/ChangeLog: * constexpr.cc (struct constexpr_call): Add NSDMIs to each field. Replace 'result' data member with 3-element 'results' array and a 'result' accessor function. Remove 'manifestly_const_eval' data member. (constexpr_call_hasher::equal): Adjust after constexpr_call layout change. (cxx_eval_call_expression): Likewise. Define some local variables closer to their first use. Use unknown_type_node instead of NULL_TREE as the "in progress" result. After successully evaluating a call with mce_unknown, also cache the result in the corresponding mce_true and mce_false slots. Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
parent
962c3d69f1
commit
5869a88144
1 changed files with 35 additions and 24 deletions
|
@ -1119,20 +1119,22 @@ explain_invalid_constexpr_fn (tree fun)
|
|||
|
||||
struct GTY((for_user)) constexpr_call {
|
||||
/* Description of the constexpr function definition. */
|
||||
constexpr_fundef *fundef;
|
||||
constexpr_fundef *fundef = nullptr;
|
||||
/* Parameter bindings environment. A TREE_VEC of arguments. */
|
||||
tree bindings;
|
||||
/* Result of the call.
|
||||
NULL means the call is being evaluated.
|
||||
tree bindings = NULL_TREE;
|
||||
/* Result of the call, indexed by the value of
|
||||
constexpr_ctx::manifestly_const_eval.
|
||||
unknown_type_node means the call is being evaluated.
|
||||
error_mark_node means that the evaluation was erroneous or otherwise
|
||||
uncacheable (e.g. because it depends on the caller).
|
||||
Otherwise, the actual value of the call. */
|
||||
tree result;
|
||||
tree results[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
|
||||
/* The hash of this call; we remember it here to avoid having to
|
||||
recalculate it when expanding the hash table. */
|
||||
hashval_t hash;
|
||||
/* The value of constexpr_ctx::manifestly_const_eval. */
|
||||
enum mce_value manifestly_const_eval;
|
||||
hashval_t hash = 0;
|
||||
|
||||
/* The result slot corresponding to the given mce_value. */
|
||||
tree& result (mce_value mce) { return results[1 + int(mce)]; }
|
||||
};
|
||||
|
||||
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
|
||||
|
@ -1427,8 +1429,6 @@ constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
|
|||
return true;
|
||||
if (lhs->hash != rhs->hash)
|
||||
return false;
|
||||
if (lhs->manifestly_const_eval != rhs->manifestly_const_eval)
|
||||
return false;
|
||||
if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
|
||||
return false;
|
||||
return cp_tree_equal (lhs->bindings, rhs->bindings);
|
||||
|
@ -2855,9 +2855,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
{
|
||||
location_t loc = cp_expr_loc_or_input_loc (t);
|
||||
tree fun = get_function_named_in_call (t);
|
||||
constexpr_call new_call
|
||||
= { NULL, NULL, NULL, 0, ctx->manifestly_const_eval };
|
||||
int depth_ok;
|
||||
|
||||
if (fun == NULL_TREE)
|
||||
return cxx_eval_internal_function (ctx, t, lval,
|
||||
|
@ -3099,7 +3096,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
if (DECL_IMMEDIATE_FUNCTION_P (fun))
|
||||
{
|
||||
new_ctx.manifestly_const_eval = mce_true;
|
||||
new_call.manifestly_const_eval = mce_true;
|
||||
ctx = &new_ctx;
|
||||
}
|
||||
|
||||
|
@ -3112,6 +3108,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
|| cp_noexcept_operand);
|
||||
|
||||
bool non_constant_args = false;
|
||||
constexpr_call new_call;
|
||||
new_call.bindings
|
||||
= cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p,
|
||||
overflow_p, &non_constant_args);
|
||||
|
@ -3185,7 +3182,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
}
|
||||
}
|
||||
|
||||
depth_ok = push_cx_call_context (t);
|
||||
int depth_ok = push_cx_call_context (t);
|
||||
|
||||
/* Remember the object we are constructing or destructing. */
|
||||
tree new_obj = NULL_TREE;
|
||||
|
@ -3227,8 +3224,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef);
|
||||
new_call.hash
|
||||
= iterative_hash_template_arg (new_call.bindings, new_call.hash);
|
||||
new_call.hash
|
||||
= iterative_hash_object (ctx->manifestly_const_eval, new_call.hash);
|
||||
|
||||
/* If we have seen this call before, we are done. */
|
||||
maybe_initialize_constexpr_call_table ();
|
||||
|
@ -3246,22 +3241,23 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
the slot can move during evaluation of the body. */
|
||||
*slot = entry = ggc_alloc<constexpr_call> ();
|
||||
*entry = new_call;
|
||||
entry->result (ctx->manifestly_const_eval) = unknown_type_node;
|
||||
fb.preserve ();
|
||||
}
|
||||
}
|
||||
/* Calls that are in progress have their result set to NULL, so that we
|
||||
can detect circular dependencies. Now that we only cache up to
|
||||
constexpr_cache_depth this won't catch circular dependencies that
|
||||
/* Calls that are in progress have their result set to unknown_type_node,
|
||||
so that we can detect circular dependencies. Now that we only cache
|
||||
up to constexpr_cache_depth this won't catch circular dependencies that
|
||||
start deeper, but they'll hit the recursion or ops limit. */
|
||||
else if (entry->result == NULL)
|
||||
else if (entry->result (ctx->manifestly_const_eval) == unknown_type_node)
|
||||
{
|
||||
if (!ctx->quiet)
|
||||
error ("call has circular dependency");
|
||||
*non_constant_p = true;
|
||||
entry->result = result = error_mark_node;
|
||||
entry->result (ctx->manifestly_const_eval) = result = error_mark_node;
|
||||
}
|
||||
else
|
||||
result = entry->result;
|
||||
result = entry->result (ctx->manifestly_const_eval);
|
||||
}
|
||||
|
||||
if (!depth_ok)
|
||||
|
@ -3482,7 +3478,22 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
else if (!result)
|
||||
result = void_node;
|
||||
if (entry)
|
||||
entry->result = cacheable ? result : error_mark_node;
|
||||
{
|
||||
entry->result (ctx->manifestly_const_eval)
|
||||
= cacheable ? result : error_mark_node;
|
||||
|
||||
if (result != error_mark_node
|
||||
&& ctx->manifestly_const_eval == mce_unknown)
|
||||
{
|
||||
/* Evaluation succeeded and was independent of whether we're in a
|
||||
manifestly constant-evaluated context, so we can also reuse
|
||||
this result when evaluating this call with a fixed context. */
|
||||
if (!entry->result (mce_true))
|
||||
entry->result (mce_true) = entry->result (mce_unknown);
|
||||
if (!entry->result (mce_false))
|
||||
entry->result (mce_false) = entry->result (mce_unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The result of a constexpr function must be completely initialized.
|
||||
|
|
Loading…
Add table
Reference in a new issue