@@ -2298,7 +2298,7 @@ static void
build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
tree orig, hash_map<tree, local_var_info> *local_var_uses,
hash_map<tree, suspend_point_info> *suspend_points,
- vec<tree, va_gc> *param_dtor_list,
+ vec<tree> *param_dtor_list,
tree resume_idx_var, unsigned body_count, tree frame_size)
{
verify_stmt_tree (fnbody);
@@ -2513,19 +2513,15 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x);
tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node);
finish_if_stmt_cond (cmp, need_free_if);
- if (param_dtor_list != NULL)
+ while (!param_dtor_list->is_empty ())
{
- int i;
- tree pid;
- FOR_EACH_VEC_ELT (*param_dtor_list, i, pid)
- {
- tree m
- = lookup_member (coro_frame_type, pid, 1, 0, tf_warning_or_error);
- tree a = build_class_member_access_expr (actor_frame, m, NULL_TREE,
- false, tf_warning_or_error);
- if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error))
- add_stmt (dtor);
- }
+ tree pid = param_dtor_list->pop ();
+ tree m = lookup_member (coro_frame_type, pid, 1, 0, tf_warning_or_error);
+ gcc_checking_assert (m);
+ tree a = build_class_member_access_expr (actor_frame, m, NULL_TREE,
+ false, tf_warning_or_error);
+ if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error))
+ add_stmt (dtor);
}
/* Build the frame DTOR. */
@@ -4553,147 +4549,28 @@ split_coroutine_body_from_ramp (tree fndecl)
return body;
}
-/* Here we:
- a) Check that the function and promise type are valid for a
- coroutine.
- b) Carry out the initial morph to create the skeleton of the
- coroutine ramp function and the rewritten body.
-
- Assumptions.
+/* Build the ramp function.
+ Here we take the original function definition which has now had its body
+ removed, and use it as the declaration of the ramp which both replaces the
+ user's written function at call sites, and is responsible for starting
+ the coroutine it defined.
+ returns NULL_TREE on error or an expression for the frame size.
- 1. We only hit this code once all dependencies are resolved.
- 2. The function body will be either a bind expr or a statement list
- 3. That cfun and current_function_decl are valid for the case we're
- expanding.
- 4. 'input_location' will be of the final brace for the function.
-
- We do something like this:
- declare a dummy coro frame.
- struct _R_frame {
- using handle_type = coro::coroutine_handle<coro1::promise_type>;
- void (*_Coro_resume_fn)(_R_frame *);
- void (*_Coro_destroy_fn)(_R_frame *);
- coro1::promise_type _Coro_promise;
- bool _Coro_frame_needs_free; free the coro frame mem if set.
- bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
- short _Coro_resume_index;
- handle_type _Coro_self_handle;
- parameter copies (were required).
- local variables saved (including awaitables)
- (maybe) trailing space.
- }; */
+ We should arrive here with the state of the compiler as if we had just
+ executed start_preparsed_function(). */
-bool
-morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
+static tree
+build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr,
+ tree coro_frame_type,
+ hash_map<tree, param_info> *param_uses,
+ tree act_des_fn_ptr, tree actor, tree destroy,
+ vec<tree> *param_dtor_list)
{
- gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
-
- *resumer = error_mark_node;
- *destroyer = error_mark_node;
- if (!coro_function_valid_p (orig))
- {
- /* For early errors, we do not want a diagnostic about the missing
- ramp return value, since the user cannot fix this - a 'return' is
- not allowed in a coroutine. */
- suppress_warning (orig, OPT_Wreturn_type);
- /* Discard the body, we can't process it further. */
- pop_stmt_list (DECL_SAVED_TREE (orig));
- DECL_SAVED_TREE (orig) = push_stmt_list ();
- /* Match the expected nesting when an eh block is in use. */
- if (use_eh_spec_block (orig))
- current_eh_spec_block = begin_eh_spec_block ();
- return false;
- }
-
- /* We don't have the locus of the opening brace - it's filled in later (and
- there doesn't really seem to be any easy way to get at it).
- The closing brace is assumed to be input_location. */
- location_t fn_start = DECL_SOURCE_LOCATION (orig);
-
- /* FIXME: This has the hidden side-effect of preparing the current function
- to be the ramp. */
- tree fnbody = split_coroutine_body_from_ramp (orig);
- if (!fnbody)
- return false;
-
- /* If the original function has a return value with a non-trivial DTOR
- and the body contains a var with a DTOR that might throw, the decl is
- marked "throwing_cleanup".
- We do not [in the ramp, which is synthesised here], use any body var
- types with DTORs that might throw.
- The original body is transformed into the actor function which only
- contains void returns, and is also wrapped in a try-catch block.
- So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do
- not need to transfer it to the actor which only contains void returns. */
- cp_function_chain->throwing_cleanup = false;
-
- /* Create the coro frame type, as far as it can be known at this stage.
- 1. Types we already know. */
-
- tree fn_return_type = TREE_TYPE (TREE_TYPE (orig));
tree promise_type = get_coroutine_promise_type (orig);
-
- /* 2. Types we need to define or look up. */
-
- tree fr_name = get_fn_local_identifier (orig, "Frame");
- tree coro_frame_type = xref_tag (record_type, fr_name);
- DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = DECL_CONTEXT (orig);
- tree coro_frame_ptr = build_pointer_type (coro_frame_type);
- tree act_des_fn_type
- = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE);
- tree act_des_fn_ptr = build_pointer_type (act_des_fn_type);
-
- /* Declare the actor and destroyer function. */
- tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
- coro_frame_ptr, true);
- tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
- coro_frame_ptr, false);
-
- /* Construct the wrapped function body; we will analyze this to determine
- the requirements for the coroutine frame. */
-
- tree resume_idx_var = NULL_TREE;
- tree fs_label = NULL_TREE;
- hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
-
- fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
- act_des_fn_ptr,
- resume_idx_var, fs_label);
- /* Build our dummy coro frame layout. */
- coro_frame_type = begin_class_definition (coro_frame_type);
-
-
- /* We need to know, and inspect, each suspend point in the function
- in several places. It's convenient to place this map out of line
- since it's used from tree walk callbacks. */
-
- hash_map<tree, suspend_point_info> suspend_points;
- /* Now insert the data for any body await points, at this time we also need
- to promote any temporaries that are captured by reference (to regular
- vars) they will get added to the coro frame along with other locals. */
- susp_frame_data body_aw_points (fs_label, &suspend_points);
- cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL);
-
- /* 4. Now make space for local vars, this is conservative again, and we
- would expect to delete unused entries later. Compose the frame entries
- list. */
-
- /* The fields for the coro frame. */
- tree field_list = NULL_TREE;
- hash_map<tree, local_var_info> local_var_uses;
- local_vars_frame_data local_vars_data (&field_list, &local_var_uses);
- cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL);
-
- /* Tie off the struct for now, so that we can build offsets to the
- known entries. */
- TYPE_FIELDS (coro_frame_type) = field_list;
- TYPE_BINFO (coro_frame_type) = make_tree_binfo (0);
- BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node;
- BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type;
-
- coro_frame_type = finish_struct (coro_frame_type, NULL_TREE);
+ tree fn_return_type = TREE_TYPE (TREE_TYPE (orig));
/* Ramp: */
+ tree stmt = begin_function_body ();
/* Now build the ramp function pieces. */
tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
add_stmt (ramp_bind);
@@ -5018,8 +4895,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
parameter copies ends immediately after the lifetime of the coroutine
promise object ends. */
- vec<tree, va_gc> *param_dtor_list = NULL;
-
if (DECL_ARGUMENTS (orig))
{
promise_args = make_tree_vector ();
@@ -5064,9 +4939,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
finish_expr_stmt (r);
if (!parm.trivial_dtor)
{
- if (param_dtor_list == NULL)
- param_dtor_list = make_tree_vector ();
- vec_safe_push (param_dtor_list, parm.field_id);
+ param_dtor_list->safe_push (parm.field_id);
/* Cleanup this frame copy on exception. */
parm.fr_copy_dtor
= cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error);
@@ -5146,7 +5019,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
/* Suppress warnings about the missing return value. */
suppress_warning (orig, OPT_Wreturn_type);
- return false;
+ return NULL_TREE;
}
tree gro_context_body = push_stmt_list ();
@@ -5318,7 +5191,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
add_stmt (promise_d_if);
}
- /* Clean up any frame copies of parms with non-trivial dtors. */
+ /* Clean up any frame copies of parms with non-trivial dtors.
+ Do this in reverse order from their creation. */
+ vec<param_info *> worklist = vNULL;
if (DECL_ARGUMENTS (orig))
for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
arg = DECL_CHAIN (arg))
@@ -5326,18 +5201,23 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
param_info *parm_i = param_uses->get (arg);
if (parm_i->trivial_dtor)
continue;
- if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node)
- {
- tree dtor_if = begin_if_stmt ();
- finish_if_stmt_cond (parm_i->guard_var, dtor_if);
- finish_expr_stmt (parm_i->fr_copy_dtor);
- finish_then_clause (dtor_if);
- tree parm_d_if_scope = IF_SCOPE (dtor_if);
- IF_SCOPE (dtor_if) = NULL;
- dtor_if = do_poplevel (parm_d_if_scope);
- add_stmt (dtor_if);
- }
+ worklist.safe_push (parm_i);
}
+ while (!worklist.is_empty())
+ {
+ param_info *parm_i = worklist.pop ();
+ if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node)
+ {
+ tree dtor_if = begin_if_stmt ();
+ finish_if_stmt_cond (parm_i->guard_var, dtor_if);
+ finish_expr_stmt (parm_i->fr_copy_dtor);
+ finish_then_clause (dtor_if);
+ tree parm_d_if_scope = IF_SCOPE (dtor_if);
+ IF_SCOPE (dtor_if) = NULL;
+ dtor_if = do_poplevel (parm_d_if_scope);
+ add_stmt (dtor_if);
+ }
+ }
/* We always expect to delete the frame. */
tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig, frame_size,
@@ -5351,7 +5231,151 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
}
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
- TREE_SIDE_EFFECTS (ramp_bind) = true;
+ finish_function_body (stmt);
+ return frame_size;
+}
+
+/* Here we:
+ a) Check that the function and promise type are valid for a
+ coroutine.
+ b) Carry out the initial morph to create the skeleton of the
+ coroutine ramp function and the rewritten body.
+
+ Assumptions.
+
+ 1. We only hit this code once all dependencies are resolved.
+ 2. The function body will be either a bind expr or a statement list
+ 3. That cfun and current_function_decl are valid for the case we're
+ expanding.
+ 4. 'input_location' will be of the final brace for the function.
+
+ We do something like this:
+ declare a dummy coro frame.
+ struct _R_frame {
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ void (*_Coro_resume_fn)(_R_frame *);
+ void (*_Coro_destroy_fn)(_R_frame *);
+ coro1::promise_type _Coro_promise;
+ bool _Coro_frame_needs_free; free the coro frame mem if set.
+ bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
+ short _Coro_resume_index;
+ handle_type _Coro_self_handle;
+ parameter copies (were required).
+ local variables saved (including awaitables)
+ (maybe) trailing space.
+ }; */
+
+bool
+morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
+{
+ gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
+
+ *resumer = error_mark_node;
+ *destroyer = error_mark_node;
+ if (!coro_function_valid_p (orig))
+ {
+ /* For early errors, we do not want a diagnostic about the missing
+ ramp return value, since the user cannot fix this - a 'return' is
+ not allowed in a coroutine. */
+ suppress_warning (orig, OPT_Wreturn_type);
+ /* Discard the body, we can't process it further. */
+ pop_stmt_list (DECL_SAVED_TREE (orig));
+ DECL_SAVED_TREE (orig) = push_stmt_list ();
+ /* Match the expected nesting when an eh block is in use. */
+ if (use_eh_spec_block (orig))
+ current_eh_spec_block = begin_eh_spec_block ();
+ return false;
+ }
+
+ /* We don't have the locus of the opening brace - it's filled in later (and
+ there doesn't really seem to be any easy way to get at it).
+ The closing brace is assumed to be input_location. */
+ location_t fn_start = DECL_SOURCE_LOCATION (orig);
+
+ /* FIXME: This has the hidden side-effect of preparing the current function
+ to be the ramp. */
+ tree fnbody = split_coroutine_body_from_ramp (orig);
+ if (!fnbody)
+ return false;
+
+ /* If the original function has a return value with a non-trivial DTOR
+ and the body contains a var with a DTOR that might throw, the decl is
+ marked "throwing_cleanup".
+ We do not [in the ramp, which is synthesised here], use any body var
+ types with DTORs that might throw.
+ The original body is transformed into the actor function which only
+ contains void returns, and is also wrapped in a try-catch block.
+ So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do
+ not need to transfer it to the actor which only contains void returns. */
+ cp_function_chain->throwing_cleanup = false;
+
+ /* Create the coro frame type, as far as it can be known at this stage.
+ 1. Types we already know. */
+
+ /* 2. Types we need to define or look up. */
+
+ tree fr_name = get_fn_local_identifier (orig, "Frame");
+ tree coro_frame_type = xref_tag (record_type, fr_name);
+ DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = DECL_CONTEXT (orig); //current_scope ();
+ tree coro_frame_ptr = build_pointer_type (coro_frame_type);
+ tree act_des_fn_type
+ = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE);
+ tree act_des_fn_ptr = build_pointer_type (act_des_fn_type);
+
+ /* Construct the wrapped function body; we will analyze this to determine
+ the requirements for the coroutine frame. */
+
+ tree resume_idx_var = NULL_TREE;
+ tree fs_label = NULL_TREE;
+ hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
+
+ fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
+ act_des_fn_ptr,
+ resume_idx_var, fs_label);
+
+ /* We need to know, and inspect, each suspend point in the function
+ in several places. It's convenient to place this map out of line
+ since it's used from tree walk callbacks. */
+
+ hash_map<tree, suspend_point_info> suspend_points;
+ /* Now insert the data for any body await points, at this time we also need
+ to promote any temporaries that are captured by reference (to regular
+ vars) they will get added to the coro frame along with other locals. */
+ susp_frame_data body_aw_points (fs_label, &suspend_points);
+ cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL);
+
+ /* 4. Now make space for local vars, this is conservative again, and we
+ would expect to delete unused entries later. Compose the frame entries
+ list. */
+
+ /* The fields for the coro frame. */
+ tree field_list = NULL_TREE;
+ hash_map<tree, local_var_info> local_var_uses;
+ local_vars_frame_data local_vars_data (&field_list, &local_var_uses);
+ cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL);
+
+ /* Tie off the struct for now, so that we can build offsets to the
+ known entries. */
+ coro_frame_type = begin_class_definition (coro_frame_type);
+ TYPE_FIELDS (coro_frame_type) = field_list;
+ TYPE_BINFO (coro_frame_type) = make_tree_binfo (0);
+ BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node;
+ BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type;
+ coro_frame_type = finish_struct (coro_frame_type, NULL_TREE);
+
+ /* Declare the actor and destroyer function. */
+ tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
+ coro_frame_ptr, true);
+ tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
+ coro_frame_ptr, false);
+
+ vec<tree> param_dtor_list = vNULL;
+ tree frame_size
+ = build_ramp_function (orig, fn_start, coro_frame_ptr, coro_frame_type,
+ param_uses, act_des_fn_ptr, actor, destroy,
+ ¶m_dtor_list);
+ if (!frame_size)
+ return false;
/* Start to build the final functions.
@@ -5362,7 +5386,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
/* Build the actor... */
build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
- &local_var_uses, &suspend_points, param_dtor_list,
+ &local_var_uses, &suspend_points, ¶m_dtor_list,
resume_idx_var, body_aw_points.await_number, frame_size);
/* Destroyer ... */
This is primarily preparation to partition the functionality of the coroutine transform into analysis, ramp generation and then (later) synthesis of the coroutine body. The patch does fix one latent issue in the ordering of DTORs for frame parameter copies (to ensure that they are processed in reverse order to the copy creation). gcc/cp/ChangeLog: * coroutines.cc (build_actor_fn): Arrange to apply any required parameter copy DTORs in reverse order to their creation. (morph_fn_to_coro): Split the ramp function completion into a separate function. (build_ramp_function): New. Signed-off-by: Iain Sandoe <iain@sandoe.co.uk> --- gcc/cp/coroutines.cc | 360 +++++++++++++++++++++++-------------------- 1 file changed, 192 insertions(+), 168 deletions(-)