return lhs->function_decl == rhs;
}
+/* Initialize the coroutine info table, to hold state per coroutine decl,
+ if not already created. */
+
+static void
+create_coroutine_info_table ()
+{
+ if (!coroutine_info_table)
+ coroutine_info_table = hash_table<coroutine_info_hasher>::create_ggc (11);
+}
+
/* Get the existing coroutine_info for FN_DECL, or insert a new one if the
entry does not yet exist. */
-coroutine_info *
+static coroutine_info *
get_or_insert_coroutine_info (tree fn_decl)
{
gcc_checking_assert (coroutine_info_table != NULL);
/* Get the existing coroutine_info for FN_DECL, fail if it doesn't exist. */
-coroutine_info *
+static coroutine_info *
get_coroutine_info (tree fn_decl)
{
if (coroutine_info_table == NULL)
if (!void_coro_handle_address)
return false;
- /* A table to hold the state, per coroutine decl. */
- gcc_checking_assert (coroutine_info_table == NULL);
- coroutine_info_table =
- hash_table<coroutine_info_hasher>::create_ggc (11);
-
+ create_coroutine_info_table ();
if (coroutine_info_table == NULL)
return false;
coro_info->self_h_proxy
= build_lang_decl (VAR_DECL, coro_self_handle_id,
coro_info->handle_type);
+ DECL_CONTEXT (coro_info->self_h_proxy) = fndecl;
/* Build a proxy for the promise so that we can perform lookups. */
coro_info->promise_proxy
= build_lang_decl (VAR_DECL, coro_promise_id,
coro_info->promise_type);
+ DECL_CONTEXT (coro_info->promise_proxy) = fndecl;
/* Note where we first saw a coroutine keyword. */
coro_info->first_coro_keyword = loc;
return NULL_TREE;
}
+/* Given a DECL, an actor or destroyer, build a link from that to the ramp
+ function. Used by modules streaming. */
+
+void
+coro_set_ramp_function (tree decl, tree ramp)
+{
+ if (!to_ramp)
+ to_ramp = hash_map<tree, tree>::create_ggc (10);
+ to_ramp->put (decl, ramp);
+}
+
/* Given the DECL for a ramp function (the user's original declaration) return
the actor function if it has been defined. */
return NULL_TREE;
}
+/* For a given ramp function DECL, set the actor and destroy functions.
+ This is only used by modules streaming. */
+
+void
+coro_set_transform_functions (tree decl, tree actor, tree destroy)
+{
+ /* Only relevant with modules. */
+ gcc_checking_assert (modules_p ());
+
+ /* This should only be called for newly streamed declarations. */
+ gcc_checking_assert (!get_coroutine_info (decl));
+
+ /* This might be the first use of coroutine info in the TU, so
+ create the coroutine info table if needed. */
+ create_coroutine_info_table ();
+
+ coroutine_info *coroutine = get_or_insert_coroutine_info (decl);
+ coroutine->actor_decl = actor;
+ coroutine->destroy_decl = destroy;
+}
+
/* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call. */
tree
= build_lang_decl (FUNCTION_DECL, copy_node (DECL_NAME (orig)), fn_type);
/* Allow for locating the ramp (original) function from this one. */
- if (!to_ramp)
- to_ramp = hash_map<tree, tree>::create_ggc (10);
- to_ramp->put (fn, orig);
+ coro_set_ramp_function (fn, orig);
DECL_CONTEXT (fn) = DECL_CONTEXT (orig);
DECL_SOURCE_LOCATION (fn) = loc;
DECL_ARTIFICIAL (fn) = true;
DECL_INITIAL (fn) = error_mark_node;
+ DECL_COROUTINE_P (fn) = true;
tree id = get_identifier ("frame_ptr");
tree fp = build_lang_decl (PARM_DECL, id, coro_frame_ptr);
/* Mergeable entity location data. */
struct merge_key {
cp_ref_qualifier ref_q : 2;
+ unsigned coro_disc : 2; /* Discriminator for coroutine transforms. */
unsigned index;
tree ret; /* Return type, if appropriate. */
tree constraints; /* Constraints. */
merge_key ()
- :ref_q (REF_QUAL_NONE), index (0),
+ :ref_q (REF_QUAL_NONE), coro_disc (0), index (0),
ret (NULL_TREE), args (NULL_TREE),
constraints (NULL_TREE)
{
else
fputs ("#null#", stream);
+ if (t && TREE_CODE (t) == FUNCTION_DECL && DECL_COROUTINE_P (t))
+ if (tree ramp = DECL_RAMP_FN (t))
+ {
+ if (DECL_ACTOR_FN (ramp) == t)
+ fputs (".actor", stream);
+ else if (DECL_DESTROY_FN (ramp) == t)
+ fputs (".destroy", stream);
+ else
+ gcc_unreachable ();
+ }
+
if (origin >= 0)
{
const module_state *module = (*modules)[origin];
return container;
}
+/* Gets a 2-bit discriminator to distinguish coroutine actor or destroy
+ functions from a normal function. */
+
+static int
+get_coroutine_discriminator (tree inner)
+{
+ if (DECL_COROUTINE_P (inner))
+ if (tree ramp = DECL_RAMP_FN (inner))
+ {
+ if (DECL_ACTOR_FN (ramp) == inner)
+ return 1;
+ else if (DECL_DESTROY_FN (ramp) == inner)
+ return 2;
+ else
+ gcc_unreachable ();
+ }
+ return 0;
+}
+
/* Write out key information about a mergeable DEP. Does not write
the contents of DEP itself. The context has already been
written. The container has already been streamed. */
tree fn_type = TREE_TYPE (inner);
key.ref_q = type_memfn_rqual (fn_type);
+ key.coro_disc = get_coroutine_discriminator (inner);
key.args = TYPE_ARG_TYPES (fn_type);
if (tree reqs = get_constraints (inner))
tree_node (name);
if (streaming_p ())
{
- unsigned code = (key.ref_q << 0) | (key.index << 2);
+ /* Check we have enough bits for the index. */
+ gcc_checking_assert (key.index < (1u << (sizeof (unsigned) * 8 - 4)));
+
+ unsigned code = ((key.ref_q << 0)
+ | (key.coro_disc << 2)
+ | (key.index << 4));
u (code);
}
}
}
-/* DECL is a new declaration that may be duplicated in OVL. Use RET &
- ARGS to find its clone, or NULL. If DECL's DECL_NAME is NULL, this
+/* DECL is a new declaration that may be duplicated in OVL. Use KEY
+ to find its clone, or NULL. If DECL's DECL_NAME is NULL, this
has been found by a proxy. It will be an enum type located by its
first member.
|| same_type_p (key.ret, fndecl_declared_return_type (m_inner)))
&& type_memfn_rqual (m_type) == key.ref_q
&& compparms (key.args, TYPE_ARG_TYPES (m_type))
+ && get_coroutine_discriminator (m_inner) == key.coro_disc
/* Reject if old is a "C" builtin and new is not "C".
Matches decls_match behaviour. */
&& (!DECL_IS_UNDECLARED_BUILTIN (m_inner)
merge_key key;
unsigned code = u ();
key.ref_q = cp_ref_qualifier ((code >> 0) & 3);
- key.index = code >> 2;
+ key.coro_disc = (code >> 2) & 3;
+ key.index = code >> 4;
if (mk == MK_enum)
key.ret = tree_node ();
if (use_tpl < 2)
return true;
}
+
+ /* Coroutine transform functions always need to be emitted
+ into the importing TU if the ramp function will be. */
+ if (DECL_COROUTINE_P (decl))
+ if (tree ramp = DECL_RAMP_FN (decl))
+ return has_definition (ramp);
break;
case TYPE_DECL:
state->write_location (*this, f->function_start_locus);
state->write_location (*this, f->function_end_locus);
}
+
+ if (DECL_COROUTINE_P (decl))
+ {
+ tree ramp = DECL_RAMP_FN (decl);
+ tree_node (ramp);
+ if (!ramp)
+ {
+ tree_node (DECL_ACTOR_FN (decl));
+ tree_node (DECL_DESTROY_FN (decl));
+ }
+ }
}
void
tree initial = tree_node ();
tree saved = tree_node ();
tree context = tree_node ();
- constexpr_fundef cexpr;
post_process_data pdata {};
pdata.decl = maybe_template;
tree maybe_dup = odr_duplicate (maybe_template, DECL_SAVED_TREE (decl));
bool installing = maybe_dup && !DECL_SAVED_TREE (decl);
+ constexpr_fundef cexpr;
if (u ())
{
cexpr.parms = chained_decls ();
cexpr.decl = NULL_TREE;
unsigned flags = u ();
-
if (flags & 2)
{
pdata.start_locus = state->read_location (*this);
pdata.infinite_loop = flags & 32;
}
+ tree coro_actor = NULL_TREE;
+ tree coro_destroy = NULL_TREE;
+ tree coro_ramp = NULL_TREE;
+ if (DECL_COROUTINE_P (decl))
+ {
+ coro_ramp = tree_node ();
+ if (!coro_ramp)
+ {
+ coro_actor = tree_node ();
+ coro_destroy = tree_node ();
+ if ((coro_actor == NULL_TREE) != (coro_destroy == NULL_TREE))
+ set_overrun ();
+ }
+ }
+
if (get_overrun ())
return NULL_TREE;
if (cexpr.decl)
register_constexpr_fundef (cexpr);
+ if (coro_ramp)
+ coro_set_ramp_function (decl, coro_ramp);
+ else if (coro_actor && coro_destroy)
+ coro_set_transform_functions (decl, coro_actor, coro_destroy);
+
if (DECL_LOCAL_DECL_P (decl))
/* Block-scope OMP UDRs aren't real functions, and don't need a
function structure to be allocated or to be expanded. */
cfun->language->returns_null = pdata.returns_null;
cfun->language->returns_abnormally = pdata.returns_abnormally;
cfun->language->infinite_loop = pdata.infinite_loop;
+ cfun->coroutine_component = DECL_COROUTINE_P (decl);
/* Make sure we emit explicit instantiations.
FIXME do we want to do this in expand_or_defer_fn instead? */