From patchwork Wed Aug 21 19:09:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975079 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=XvEfKEp2; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WpwsJ4Fb6z1yXf for ; Thu, 22 Aug 2024 05:10:44 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E697838708FA for ; Wed, 21 Aug 2024 19:10:41 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by sourceware.org (Postfix) with ESMTPS id 586F63858C52 for ; Wed, 21 Aug 2024 19:10:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 586F63858C52 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 586F63858C52 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::633 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267413; cv=none; b=lQvQv4FBNQtzw7ot7zr+7yc7wfhTlusXj1QT/ToCo6L+i8a3g7vD9hzl2lz9Ru3hDCjHNh7lB+bISPUxOBzELPVXMKPEQoJMVGsDE4HOKmvTeV6n5dcEdPdE7GKl2bWLGKjRtid+UNQ+Uv5gYuafPFZ6BJcEZQ91EAENPTwADtU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267413; c=relaxed/simple; bh=RvcAhM+DFyioO76i1jYMrGQwVKkXA7e1UqmYtkd7RZs=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=YnBSk2YAEVeoFDOAAiW7VmeX4m6qyP3nFQMmQzDDqbo3GHCbgh/lKvRWpQCvJlT4Ly0plcf4l4uQwlKMLJ597tWOeiowwkp7GL2GRe76UkAktQvl68RqMNmzaqmwzyQ6WkLH5pl/v0roecvgmir8zwJjGiMVf5s9HGQ458FN+Es= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x633.google.com with SMTP id a640c23a62f3a-a7a9cf7d3f3so904220666b.1 for ; Wed, 21 Aug 2024 12:10:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267409; x=1724872209; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=hRSVw+nRk8DkP88cF9Go2Yn8arcN3D7TH5jC1zDTj60=; b=XvEfKEp2dCMJQuymMtgRzuY0tWX9WFkjSzCg/0vR8EAswmU4hByrLMeb+msUv8s5ZN lH2jkj98dBu/00Hun7VW4s7yqrqaOzsHHFUB6LVFAvPM0eZTS9nqjfeGB3JkVDgmbw2/ 8VGFWyD+bB13xqMFdN7cHXaqDOXP41jQm64fHCgYdWlvAIZYUFdt5tkPErvkYNm46/hM FYlwW4wJppAgHq5dVOSp8jylKlWmgRb0y9C7ccDfpay+gIIEVBfoRxtj687IGHMZRjF4 WkeiXcE/o99SVKLq0y07pNDe6oN0jMK0kaU78JWVuwL5JZ1OJ1zXZD/z+KOggPxtGBpI OnPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267409; x=1724872209; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=hRSVw+nRk8DkP88cF9Go2Yn8arcN3D7TH5jC1zDTj60=; b=jSI9VC1Zcr+qAj+aiJH/ulKYmGIbLFGWNIcl2he40GjSMk7YFf9HgUbE8AxXWHKgik ZC9S+AnpyxdoG2Hsz5l6z/VFrLa5pM9PhMsztOanv8fgARzZqphyaVJISoox2E/0bVWG +CzHGsRUqMjgqBlWo54PnYWliYWuM1KCQ7tethdfjJ3cQTMqIV5g1cQMpDuPXp6Nkrfk /ETIOxsw7x+KccJrGdwAZvPqtV7ebcfNknUrEoLb2y+PciNs3gIu5ESTUbnxiZlaiKXE 92hombG5ISK0JtPFbWbAm/FNEI73jmsS8ckBlkrWSX/jQ3wa1rwW4Y8oFBQMTxdy1BB6 OHJw== X-Gm-Message-State: AOJu0YyY6RqEBgGXEom67bs4JR13oRuPa47zQrMBNoiDz91iz1Q7ulCq OShF0oBYa+wimG4cnhRs6t1+lcHVUFcx5QeU4Q5S9bnSVsxFNBjrPWfLA/Ut X-Google-Smtp-Source: AGHT+IE4Sd8UTuS002FLC+d7smY+/lVve4CIal0Phb7wE8xoNzS7YkfvO0EY23u/H6aQFHAqn3VclA== X-Received: by 2002:a17:906:c14f:b0:a77:b516:3023 with SMTP id a640c23a62f3a-a866f11d8cfmr196161766b.4.1724267408409; Wed, 21 Aug 2024 12:10:08 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.07 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:08 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 1/9] c++, coroutines: Split the ramp build into a separate function. Date: Wed, 21 Aug 2024 20:09:58 +0100 Message-Id: <1863dcd621d308ebae10fb0adcd35b541b3fb4e8.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org 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 --- gcc/cp/coroutines.cc | 360 +++++++++++++++++++++++-------------------- 1 file changed, 192 insertions(+), 168 deletions(-) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 1f1ea5c2fe4..50362fc3556 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -2298,7 +2298,7 @@ static void build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, tree orig, hash_map *local_var_uses, hash_map *suspend_points, - vec *param_dtor_list, + vec *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; - 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 *param_uses, + tree act_des_fn_ptr, tree actor, tree destroy, + vec *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 *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 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 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 *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 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; + 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 *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 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 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 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 ... */