GCC with patches for OS216
修訂 | b1c3d3111a0ac36ae3c2d7412d20777fc073fdeb (tree) |
---|---|
時間 | 2020-07-02 02:05:13 |
作者 | Iain Sandoe <iain@sand...> |
Commiter | Iain Sandoe |
coroutines: Collect the function body rewrite code.
The standard describes a rewrite of the body of the user-authored
function (which wraps it in a try-catch block and provides the
initial and final suspend expressions). The exact arrangement of
this was still in flux right up until the DIS and as a consequence
was a bit of a moving target.
The net result was a fragmented implementation of the parts of
the rewrite which is now impeding progress in fixing other issues.
This patch collates the rewrite action into a single function and
carries this out earlier.
gcc/cp/ChangeLog:
* coroutines.cc (expand_one_await_expression): Remove
code dealing with initial suspend.
(build_actor_fn): Remove code special-casing initial
and final suspend. Handle the final suspend and marking
of the coroutine as done.
(coro_rewrite_function_body): New.
(bind_expr_find_in_subtree): Remove.
(coro_body_contains_bind_expr_p): Remove.
(morph_fn_to_coro): Split the rewrite of the original
function into coro_rewrite_function_body and call it.
(cherry picked from commit 9252a208f485eed2757d601657facfa0aee6cd21)
@@ -1576,8 +1576,6 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) | ||
1576 | 1576 | tree awaiter_calls = TREE_OPERAND (saved_co_await, 3); |
1577 | 1577 | |
1578 | 1578 | tree source = TREE_OPERAND (saved_co_await, 4); |
1579 | - bool is_initial = | |
1580 | - (source && TREE_INT_CST_LOW (source) == (int) INITIAL_SUSPEND_POINT); | |
1581 | 1579 | bool is_final = (source |
1582 | 1580 | && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); |
1583 | 1581 | bool needs_dtor = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)); |
@@ -1727,16 +1725,6 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) | ||
1727 | 1725 | resume_label = build_stmt (loc, LABEL_EXPR, resume_label); |
1728 | 1726 | append_to_statement_list (resume_label, &stmt_list); |
1729 | 1727 | |
1730 | - if (is_initial) | |
1731 | - { | |
1732 | - /* Note that we are about to execute the await_resume() for the initial | |
1733 | - await expression. */ | |
1734 | - r = build2_loc (loc, MODIFY_EXPR, boolean_type_node, data->i_a_r_c, | |
1735 | - boolean_true_node); | |
1736 | - r = coro_build_cvt_void_expr_stmt (r, loc); | |
1737 | - append_to_statement_list (r, &stmt_list); | |
1738 | - } | |
1739 | - | |
1740 | 1728 | /* This will produce the value (if one is provided) from the co_await |
1741 | 1729 | expression. */ |
1742 | 1730 | tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */ |
@@ -2103,19 +2091,16 @@ static void | ||
2103 | 2091 | build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, |
2104 | 2092 | tree orig, hash_map<tree, param_info> *param_uses, |
2105 | 2093 | hash_map<tree, local_var_info> *local_var_uses, |
2106 | - vec<tree, va_gc> *param_dtor_list, tree initial_await, | |
2107 | - tree final_await, unsigned body_count, tree frame_size) | |
2094 | + vec<tree, va_gc> *param_dtor_list, | |
2095 | + tree fs_label, tree resume_fn_field, | |
2096 | + unsigned body_count, tree frame_size) | |
2108 | 2097 | { |
2109 | 2098 | verify_stmt_tree (fnbody); |
2110 | 2099 | /* Some things we inherit from the original function. */ |
2111 | - tree coro_frame_ptr = build_pointer_type (coro_frame_type); | |
2112 | 2100 | tree handle_type = get_coroutine_handle_type (orig); |
2113 | 2101 | tree self_h_proxy = get_coroutine_self_handle_proxy (orig); |
2114 | 2102 | tree promise_type = get_coroutine_promise_type (orig); |
2115 | 2103 | tree promise_proxy = get_coroutine_promise_proxy (orig); |
2116 | - tree act_des_fn_type | |
2117 | - = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE); | |
2118 | - tree act_des_fn_ptr = build_pointer_type (act_des_fn_type); | |
2119 | 2104 | |
2120 | 2105 | /* One param, the coro frame pointer. */ |
2121 | 2106 | tree actor_fp = DECL_ARGUMENTS (actor); |
@@ -2146,21 +2131,15 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2146 | 2131 | DECL_CONTEXT (continuation) = actor; |
2147 | 2132 | BIND_EXPR_VARS (actor_bind) = continuation; |
2148 | 2133 | |
2149 | - /* Update the block associated with the outer scope of the orig fn. */ | |
2134 | + /* Link in the block associated with the outer scope of the re-written | |
2135 | + function body. */ | |
2150 | 2136 | tree first = expr_first (fnbody); |
2151 | - if (first && TREE_CODE (first) == BIND_EXPR) | |
2152 | - { | |
2153 | - /* We will discard this, since it's connected to the original scope | |
2154 | - nest. */ | |
2155 | - tree block = BIND_EXPR_BLOCK (first); | |
2156 | - if (block) /* For this to be missing is probably a bug. */ | |
2157 | - { | |
2158 | - gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
2159 | - gcc_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
2160 | - BLOCK_SUPERCONTEXT (block) = top_block; | |
2161 | - BLOCK_SUBBLOCKS (top_block) = block; | |
2162 | - } | |
2163 | - } | |
2137 | + gcc_checking_assert (first && TREE_CODE (first) == BIND_EXPR); | |
2138 | + tree block = BIND_EXPR_BLOCK (first); | |
2139 | + gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
2140 | + gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
2141 | + BLOCK_SUPERCONTEXT (block) = top_block; | |
2142 | + BLOCK_SUBBLOCKS (top_block) = block; | |
2164 | 2143 | |
2165 | 2144 | add_stmt (actor_bind); |
2166 | 2145 | tree actor_body = push_stmt_list (); |
@@ -2237,7 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2237 | 2216 | add_stmt (b); |
2238 | 2217 | |
2239 | 2218 | short unsigned lab_num = 3; |
2240 | - for (unsigned destr_pt = 0; destr_pt < body_count + 2; destr_pt++) | |
2219 | + for (unsigned destr_pt = 0; destr_pt < body_count; destr_pt++) | |
2241 | 2220 | { |
2242 | 2221 | tree l_num = build_int_cst (short_unsigned_type_node, lab_num); |
2243 | 2222 | b = build_case_label (l_num, NULL_TREE, |
@@ -2273,8 +2252,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2273 | 2252 | add_stmt (b); |
2274 | 2253 | |
2275 | 2254 | lab_num = 2; |
2276 | - /* The final resume should be made to hit the default (trap, UB) entry. */ | |
2277 | - for (unsigned resu_pt = 0; resu_pt < body_count + 1; resu_pt++) | |
2255 | + /* The final resume should be made to hit the default (trap, UB) entry | |
2256 | + although it will be unreachable via the normal entry point, since that | |
2257 | + is set to NULL on reaching final suspend. */ | |
2258 | + for (unsigned resu_pt = 0; resu_pt < body_count; resu_pt++) | |
2278 | 2259 | { |
2279 | 2260 | tree l_num = build_int_cst (short_unsigned_type_node, lab_num); |
2280 | 2261 | b = build_case_label (l_num, NULL_TREE, |
@@ -2327,55 +2308,19 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2327 | 2308 | await_xform_data xform |
2328 | 2309 | = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash}; |
2329 | 2310 | |
2330 | - /* Get a reference to the initial suspend var in the frame. */ | |
2331 | - transform_await_expr (initial_await, &xform); | |
2332 | - tree initial_await_stmt = coro_build_expr_stmt (initial_await, loc); | |
2333 | - | |
2334 | - /* co_return branches to the final_suspend label, so declare that now. */ | |
2335 | - tree fs_label = create_named_label_with_ctx (loc, "final.suspend", actor); | |
2336 | - | |
2337 | 2311 | /* Expand co_returns in the saved function body */ |
2338 | 2312 | fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label); |
2339 | 2313 | |
2340 | - /* n4849 adds specific behaviour to treat exceptions thrown by the | |
2341 | - await_resume () of the initial suspend expression. In order to | |
2342 | - implement this, we need to treat the initial_suspend expression | |
2343 | - as if it were part of the user-authored function body. This | |
2344 | - only applies if exceptions are enabled. */ | |
2345 | - if (flag_exceptions) | |
2346 | - { | |
2347 | - tree outer = fnbody; | |
2348 | - if (TREE_CODE (outer) == BIND_EXPR) | |
2349 | - outer = BIND_EXPR_BODY (outer); | |
2350 | - gcc_checking_assert (TREE_CODE (outer) == TRY_BLOCK); | |
2351 | - tree sl = TRY_STMTS (outer); | |
2352 | - if (TREE_CODE (sl) == STATEMENT_LIST) | |
2353 | - { | |
2354 | - tree_stmt_iterator si = tsi_start (sl); | |
2355 | - tsi_link_before (&si, initial_await_stmt, TSI_NEW_STMT); | |
2356 | - } | |
2357 | - else | |
2358 | - { | |
2359 | - tree new_try = NULL_TREE; | |
2360 | - append_to_statement_list (initial_await_stmt, &new_try); | |
2361 | - append_to_statement_list (sl, &new_try); | |
2362 | - TRY_STMTS (outer) = new_try; | |
2363 | - } | |
2364 | - } | |
2365 | - else | |
2366 | - add_stmt (initial_await_stmt); | |
2367 | - | |
2368 | 2314 | /* Transform the await expressions in the function body. Only do each |
2369 | 2315 | await tree once! */ |
2370 | 2316 | hash_set<tree> pset; |
2371 | 2317 | cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset); |
2372 | 2318 | |
2373 | - /* Add in our function body with the co_returns rewritten to final form. */ | |
2374 | - add_stmt (fnbody); | |
2375 | - | |
2376 | - /* Final suspend starts here. */ | |
2377 | - r = build_stmt (loc, LABEL_EXPR, fs_label); | |
2378 | - add_stmt (r); | |
2319 | + /* Now replace the promise proxy with its real value. */ | |
2320 | + proxy_replace p_data; | |
2321 | + p_data.from = promise_proxy; | |
2322 | + p_data.to = ap; | |
2323 | + cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL); | |
2379 | 2324 | |
2380 | 2325 | /* Set the actor pointer to null, so that 'done' will work. |
2381 | 2326 | Resume from here is UB anyway - although a 'ready' await will |
@@ -2385,15 +2330,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2385 | 2330 | /*protect=*/1, /*want_type=*/0, tf_warning_or_error); |
2386 | 2331 | tree res_x = build_class_member_access_expr (actor_frame, resume_m, NULL_TREE, |
2387 | 2332 | false, tf_warning_or_error); |
2388 | - r = build1 (CONVERT_EXPR, act_des_fn_ptr, integer_zero_node); | |
2389 | - r = build2 (INIT_EXPR, act_des_fn_ptr, res_x, r); | |
2390 | - r = coro_build_cvt_void_expr_stmt (r, loc); | |
2391 | - add_stmt (r); | |
2333 | + p_data.from = resume_fn_field; | |
2334 | + p_data.to = res_x; | |
2335 | + cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL); | |
2392 | 2336 | |
2393 | - /* Get a reference to the final suspend var in the frame. */ | |
2394 | - transform_await_expr (final_await, &xform); | |
2395 | - r = coro_build_expr_stmt (final_await, loc); | |
2396 | - add_stmt (r); | |
2337 | + /* Add in our function body with the co_returns rewritten to final form. */ | |
2338 | + add_stmt (fnbody); | |
2397 | 2339 | |
2398 | 2340 | /* now do the tail of the function. */ |
2399 | 2341 | tree del_promise_label |
@@ -2560,20 +2502,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | ||
2560 | 2502 | = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false, |
2561 | 2503 | tf_warning_or_error); |
2562 | 2504 | |
2563 | - /* Boolean value to flag that the initial suspend expression's | |
2564 | - await_resume () has been called, and therefore we are in the user's | |
2565 | - function body for the purposes of handing exceptions. */ | |
2566 | - tree i_a_r_c_m | |
2567 | - = lookup_member (coro_frame_type, get_identifier ("__i_a_r_c"), | |
2568 | - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); | |
2569 | - tree i_a_r_c | |
2570 | - = build_class_member_access_expr (actor_frame, i_a_r_c_m, NULL_TREE, | |
2571 | - false, tf_warning_or_error); | |
2572 | - | |
2573 | 2505 | /* We've now rewritten the tree and added the initial and final |
2574 | 2506 | co_awaits. Now pass over the tree and expand the co_awaits. */ |
2575 | 2507 | |
2576 | - coro_aw_data data = {actor, actor_fp, resume_pt_number, i_a_r_c, | |
2508 | + coro_aw_data data = {actor, actor_fp, resume_pt_number, NULL_TREE, | |
2577 | 2509 | ash, del_promise_label, ret_label, |
2578 | 2510 | continue_label, continuation, 2}; |
2579 | 2511 | cp_walk_tree (&actor_body, await_statement_expander, &data, NULL); |
@@ -3642,23 +3574,165 @@ act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name) | ||
3642 | 3574 | return fn; |
3643 | 3575 | } |
3644 | 3576 | |
3645 | -/* Return a bind expression if we see one, else NULL_TREE. */ | |
3577 | +/* Re-write the body as per [dcl.fct.def.coroutine] / 5. */ | |
3578 | + | |
3646 | 3579 | static tree |
3647 | -bind_expr_find_in_subtree (tree *stmt, int *, void *) | |
3580 | +coro_rewrite_function_body (location_t fn_start, tree fnbody, | |
3581 | + tree orig, tree resume_fn_ptr_type, | |
3582 | + tree& resume_fn_field, tree& fs_label) | |
3648 | 3583 | { |
3649 | - if (TREE_CODE (*stmt) == BIND_EXPR) | |
3650 | - return *stmt; | |
3651 | - return NULL_TREE; | |
3652 | -} | |
3584 | + /* This will be our new outer scope. */ | |
3585 | + tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); | |
3586 | + tree top_block = make_node (BLOCK); | |
3587 | + BIND_EXPR_BLOCK (update_body) = top_block; | |
3588 | + BIND_EXPR_BODY (update_body) = push_stmt_list (); | |
3653 | 3589 | |
3654 | -/* Return the first bind expression that the sub-tree given by STMT | |
3655 | - contains. */ | |
3590 | + /* If the function has a top level bind expression, then connect that | |
3591 | + after first making sure we give it a new block. */ | |
3592 | + tree first = expr_first (fnbody); | |
3593 | + if (first && TREE_CODE (first) == BIND_EXPR) | |
3594 | + { | |
3595 | + tree block = BIND_EXPR_BLOCK (first); | |
3596 | + gcc_checking_assert (block); | |
3597 | + gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
3598 | + gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
3599 | + /* Replace the top block to avoid issues with locations for args | |
3600 | + appearing to be in a non-existent place. */ | |
3601 | + tree replace_blk = make_node (BLOCK); | |
3602 | + BLOCK_VARS (replace_blk) = BLOCK_VARS (block); | |
3603 | + BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block); | |
3604 | + for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b)) | |
3605 | + BLOCK_SUPERCONTEXT (b) = replace_blk; | |
3606 | + BIND_EXPR_BLOCK (first) = replace_blk; | |
3607 | + /* The top block has one child, so far, and we have now got a | |
3608 | + superblock. */ | |
3609 | + BLOCK_SUPERCONTEXT (block) = top_block; | |
3610 | + BLOCK_SUBBLOCKS (top_block) = block; | |
3611 | + } | |
3656 | 3612 | |
3657 | -static tree | |
3658 | -coro_body_contains_bind_expr_p (tree *stmt) | |
3659 | -{ | |
3660 | - hash_set<tree> visited; | |
3661 | - return cp_walk_tree (stmt, bind_expr_find_in_subtree, NULL, &visited); | |
3613 | + /* Wrap the function body in a try {} catch (...) {} block, if exceptions | |
3614 | + are enabled. */ | |
3615 | + tree promise = get_coroutine_promise_proxy (orig); | |
3616 | + tree var_list = NULL_TREE; | |
3617 | + tree initial_await = build_init_or_final_await (fn_start, false); | |
3618 | + | |
3619 | + /* [stmt.return.coroutine] / 3 | |
3620 | + If p.return_void() is a valid expression, flowing off the end of a | |
3621 | + coroutine is equivalent to a co_return with no operand; otherwise | |
3622 | + flowing off the end of a coroutine results in undefined behavior. */ | |
3623 | + tree return_void | |
3624 | + = get_coroutine_return_void_expr (current_function_decl, fn_start, false); | |
3625 | + | |
3626 | + if (flag_exceptions) | |
3627 | + { | |
3628 | + /* Build promise.unhandled_exception(); */ | |
3629 | + tree ueh | |
3630 | + = coro_build_promise_expression (current_function_decl, promise, | |
3631 | + coro_unhandled_exception_identifier, | |
3632 | + fn_start, NULL, /*musthave=*/true); | |
3633 | + /* Create and initialize the initial-await-resume-called variable per | |
3634 | + [dcl.fct.def.coroutine] / 5.3. */ | |
3635 | + tree i_a_r_c = build_lang_decl (VAR_DECL, get_identifier ("__i_a_r_c"), | |
3636 | + boolean_type_node); | |
3637 | + DECL_ARTIFICIAL (i_a_r_c) = true; | |
3638 | + DECL_CHAIN (i_a_r_c) = var_list; | |
3639 | + var_list = i_a_r_c; | |
3640 | + DECL_INITIAL (i_a_r_c) = boolean_false_node; | |
3641 | + add_decl_expr (i_a_r_c); | |
3642 | + /* Start the try-catch. */ | |
3643 | + tree tcb = build_stmt (fn_start, TRY_BLOCK, NULL_TREE, NULL_TREE); | |
3644 | + add_stmt (tcb); | |
3645 | + TRY_STMTS (tcb) = push_stmt_list (); | |
3646 | + if (initial_await != error_mark_node) | |
3647 | + { | |
3648 | + /* Build a compound expression that sets the | |
3649 | + initial-await-resume-called variable true and then calls the | |
3650 | + initial suspend expression await resume. */ | |
3651 | + tree vec = TREE_OPERAND (initial_await, 3); | |
3652 | + tree aw_r = TREE_VEC_ELT (vec, 2); | |
3653 | + tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, | |
3654 | + boolean_true_node); | |
3655 | + aw_r = cp_build_compound_expr (update, aw_r, tf_warning_or_error); | |
3656 | + TREE_VEC_ELT (vec, 2) = aw_r; | |
3657 | + } | |
3658 | + /* Add the initial await to the start of the user-authored function. */ | |
3659 | + finish_expr_stmt (initial_await); | |
3660 | + /* Append the original function body. */ | |
3661 | + add_stmt (fnbody); | |
3662 | + if (return_void) | |
3663 | + add_stmt (return_void); | |
3664 | + TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb)); | |
3665 | + TRY_HANDLERS (tcb) = push_stmt_list (); | |
3666 | + /* Mimic what the parser does for the catch. */ | |
3667 | + tree handler = begin_handler (); | |
3668 | + finish_handler_parms (NULL_TREE, handler); /* catch (...) */ | |
3669 | + | |
3670 | + /* Get the initial await resume called value. */ | |
3671 | + tree not_iarc_if = begin_if_stmt (); | |
3672 | + tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, | |
3673 | + boolean_type_node, i_a_r_c); | |
3674 | + finish_if_stmt_cond (not_iarc, not_iarc_if); | |
3675 | + /* If the initial await resume called value is false, rethrow... */ | |
3676 | + tree rethrow = build_throw (fn_start, NULL_TREE); | |
3677 | + TREE_NO_WARNING (rethrow) = true; | |
3678 | + finish_expr_stmt (rethrow); | |
3679 | + finish_then_clause (not_iarc_if); | |
3680 | + tree iarc_scope = IF_SCOPE (not_iarc_if); | |
3681 | + IF_SCOPE (not_iarc_if) = NULL; | |
3682 | + not_iarc_if = do_poplevel (iarc_scope); | |
3683 | + add_stmt (not_iarc_if); | |
3684 | + /* ... else call the promise unhandled exception method. */ | |
3685 | + ueh = maybe_cleanup_point_expr_void (ueh); | |
3686 | + add_stmt (ueh); | |
3687 | + finish_handler (handler); | |
3688 | + TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb)); | |
3689 | + } | |
3690 | + else | |
3691 | + { | |
3692 | + if (pedantic) | |
3693 | + { | |
3694 | + /* We still try to look for the promise method and warn if it's not | |
3695 | + present. */ | |
3696 | + tree ueh_meth | |
3697 | + = lookup_promise_method (orig, coro_unhandled_exception_identifier, | |
3698 | + fn_start, /*musthave=*/false); | |
3699 | + if (!ueh_meth || ueh_meth == error_mark_node) | |
3700 | + warning_at (fn_start, 0, "no member named %qE in %qT", | |
3701 | + coro_unhandled_exception_identifier, | |
3702 | + get_coroutine_promise_type (orig)); | |
3703 | + } | |
3704 | + /* Else we don't check and don't care if the method is missing.. | |
3705 | + just add the initial suspend, function and return. */ | |
3706 | + finish_expr_stmt (initial_await); | |
3707 | + /* Append the original function body. */ | |
3708 | + add_stmt (fnbody); | |
3709 | + if (return_void) | |
3710 | + add_stmt (return_void); | |
3711 | + } | |
3712 | + | |
3713 | + /* co_return branches to the final_suspend label, so declare that now. */ | |
3714 | + fs_label | |
3715 | + = create_named_label_with_ctx (fn_start, "final.suspend", NULL_TREE); | |
3716 | + add_stmt (build_stmt (fn_start, LABEL_EXPR, fs_label)); | |
3717 | + | |
3718 | + /* Before entering the final suspend point, we signal that this point has | |
3719 | + been reached by setting the resume function pointer to zero (this is | |
3720 | + what the 'done()' builtin tests) as per the current ABI. */ | |
3721 | + resume_fn_field | |
3722 | + = build_lang_decl (VAR_DECL, get_identifier ("resume.fn.ptr.proxy"), | |
3723 | + resume_fn_ptr_type); | |
3724 | + DECL_ARTIFICIAL (resume_fn_field) = true; | |
3725 | + tree zero_resume | |
3726 | + = build1 (CONVERT_EXPR, resume_fn_ptr_type, integer_zero_node); | |
3727 | + zero_resume | |
3728 | + = build2 (INIT_EXPR, resume_fn_ptr_type, resume_fn_field, zero_resume); | |
3729 | + finish_expr_stmt (zero_resume); | |
3730 | + finish_expr_stmt (build_init_or_final_await (fn_start, true)); | |
3731 | + BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); | |
3732 | + BIND_EXPR_VARS (update_body) = nreverse (var_list); | |
3733 | + BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); | |
3734 | + | |
3735 | + return update_body; | |
3662 | 3736 | } |
3663 | 3737 | |
3664 | 3738 | /* Here we: |
@@ -3735,55 +3809,32 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
3735 | 3809 | return false; |
3736 | 3810 | } |
3737 | 3811 | |
3738 | - /* So, we've tied off the original body. Now start the replacement. | |
3812 | + /* So, we've tied off the original user-authored body in fn_body. | |
3813 | + | |
3814 | + Start the replacement synthesized ramp body as newbody. | |
3739 | 3815 | If we encounter a fatal error we might return a now-empty body. |
3740 | - TODO: determine if it would help to restore the original. | |
3741 | - determine if looking for more errors in coro_function_valid_p() | |
3742 | - and stashing types is a better solution. */ | |
3816 | + | |
3817 | + Note, the returned ramp body is not 'popped', to be compatible with | |
3818 | + the way that decl.c handles regular functions, the scope pop is done | |
3819 | + in the caller. */ | |
3743 | 3820 | |
3744 | 3821 | tree newbody = push_stmt_list (); |
3745 | 3822 | DECL_SAVED_TREE (orig) = newbody; |
3746 | 3823 | |
3747 | 3824 | /* If our original body is noexcept, then that's what we apply to our |
3748 | - generated functions. Remember that we're NOEXCEPT and fish out the | |
3749 | - contained list (we tied off to the top level already). */ | |
3825 | + generated ramp, transfer any MUST_NOT_THOW_EXPR to that. */ | |
3750 | 3826 | bool is_noexcept = TREE_CODE (body_start) == MUST_NOT_THROW_EXPR; |
3751 | 3827 | if (is_noexcept) |
3752 | 3828 | { |
3753 | - /* Simplified abstract from begin_eh_spec_block, since we already | |
3754 | - know the outcome. */ | |
3755 | - fnbody = TREE_OPERAND (body_start, 0); /* Stash the original... */ | |
3756 | - add_stmt (body_start); /* ... and start the new. */ | |
3829 | + /* The function body we will continue with is the single operand to | |
3830 | + the must-not-throw. */ | |
3831 | + fnbody = TREE_OPERAND (body_start, 0); | |
3832 | + /* Transfer the must-not-throw to the ramp body. */ | |
3833 | + add_stmt (body_start); | |
3834 | + /* Re-start the ramp as must-not-throw. */ | |
3757 | 3835 | TREE_OPERAND (body_start, 0) = push_stmt_list (); |
3758 | 3836 | } |
3759 | 3837 | |
3760 | - /* We can be presented with a function that currently has no outer bind | |
3761 | - expression. We will insert bind scopes in expanding await expressions, | |
3762 | - and therefore need a top level to the tree, so synthesize an outer bind | |
3763 | - expression and scope. */ | |
3764 | - tree check_bind = expr_first (fnbody); | |
3765 | - if (check_bind && TREE_CODE (check_bind) != BIND_EXPR) | |
3766 | - { | |
3767 | - tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); | |
3768 | - tree blk = make_node (BLOCK); | |
3769 | - gcc_checking_assert (!coro_body_contains_bind_expr_p (&fnbody)); | |
3770 | - BIND_EXPR_BLOCK (update_body) = blk; | |
3771 | - if (TREE_CODE (fnbody) == STATEMENT_LIST) | |
3772 | - BIND_EXPR_BODY (update_body) = fnbody; | |
3773 | - else | |
3774 | - { | |
3775 | - tree tlist = NULL_TREE; | |
3776 | - append_to_statement_list_force (fnbody, &tlist); | |
3777 | - TREE_SIDE_EFFECTS (tlist) = TREE_SIDE_EFFECTS (fnbody); | |
3778 | - BIND_EXPR_BODY (update_body) = tlist; | |
3779 | - } | |
3780 | - tree new_body_list = NULL_TREE; | |
3781 | - TREE_SIDE_EFFECTS (update_body) = true; | |
3782 | - append_to_statement_list (update_body, &new_body_list); | |
3783 | - TREE_SIDE_EFFECTS (new_body_list) = true; | |
3784 | - fnbody = new_body_list; | |
3785 | - } | |
3786 | - | |
3787 | 3838 | /* Create the coro frame type, as far as it can be known at this stage. |
3788 | 3839 | 1. Types we already know. */ |
3789 | 3840 |
@@ -3793,35 +3844,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
3793 | 3844 | |
3794 | 3845 | /* 2. Types we need to define or look up. */ |
3795 | 3846 | |
3796 | - /* We need to know, and inspect, each suspend point in the function | |
3797 | - in several places. It's convenient to place this map out of line | |
3798 | - since it's used from tree walk callbacks. */ | |
3799 | - suspend_points = new hash_map<tree, suspend_point_info>; | |
3800 | - | |
3801 | - /* Initial and final suspend types are special in that the co_awaits for | |
3802 | - them are synthetic. We need to find the type for each awaiter from | |
3803 | - the coroutine promise. */ | |
3804 | - tree initial_await = build_init_or_final_await (fn_start, false); | |
3805 | - if (initial_await == error_mark_node) | |
3806 | - { | |
3807 | - /* Suppress warnings about the missing return value. */ | |
3808 | - TREE_NO_WARNING (orig) = true; | |
3809 | - return false; | |
3810 | - } | |
3811 | - /* The type of the frame var for this is the type of its temp proxy. */ | |
3812 | - tree initial_suspend_type = TREE_TYPE (TREE_OPERAND (initial_await, 1)); | |
3813 | - | |
3814 | - tree final_await = build_init_or_final_await (fn_start, true); | |
3815 | - if (final_await == error_mark_node) | |
3816 | - { | |
3817 | - /* Suppress warnings about the missing return value. */ | |
3818 | - TREE_NO_WARNING (orig) = true; | |
3819 | - return false; | |
3820 | - } | |
3821 | - | |
3822 | - /* The type of the frame var for this is the type of its temp proxy. */ | |
3823 | - tree final_suspend_type = TREE_TYPE (TREE_OPERAND (final_await, 1)); | |
3824 | - | |
3825 | 3847 | tree fr_name = get_fn_local_identifier (orig, "frame"); |
3826 | 3848 | tree coro_frame_type = xref_tag (record_type, fr_name, ts_current, false); |
3827 | 3849 | DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope (); |
@@ -3834,20 +3856,27 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
3834 | 3856 | tree actor = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "actor"); |
3835 | 3857 | tree destroy = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "destroy"); |
3836 | 3858 | |
3859 | + /* Construct the wrapped function body; we will analyze this to determine | |
3860 | + the requirements for the coroutine frame. */ | |
3861 | + | |
3862 | + tree resume_fn_field = NULL_TREE; | |
3863 | + tree fs_label = NULL_TREE; | |
3864 | + fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, act_des_fn_ptr, | |
3865 | + resume_fn_field, fs_label); | |
3837 | 3866 | /* Build our dummy coro frame layout. */ |
3838 | 3867 | coro_frame_type = begin_class_definition (coro_frame_type); |
3839 | 3868 | |
3840 | 3869 | tree field_list = NULL_TREE; |
3841 | 3870 | tree resume_name |
3842 | - = coro_make_frame_entry (&field_list, "__resume", act_des_fn_ptr, fn_start); | |
3843 | - tree destroy_name = coro_make_frame_entry (&field_list, "__destroy", | |
3844 | - act_des_fn_ptr, fn_start); | |
3871 | + = coro_make_frame_entry (&field_list, "__resume", | |
3872 | + act_des_fn_ptr, fn_start); | |
3873 | + tree destroy_name | |
3874 | + = coro_make_frame_entry (&field_list, "__destroy", | |
3875 | + act_des_fn_ptr, fn_start); | |
3845 | 3876 | tree promise_name |
3846 | 3877 | = coro_make_frame_entry (&field_list, "__p", promise_type, fn_start); |
3847 | 3878 | tree fnf_name = coro_make_frame_entry (&field_list, "__frame_needs_free", |
3848 | 3879 | boolean_type_node, fn_start); |
3849 | - tree iarc_name = coro_make_frame_entry (&field_list, "__i_a_r_c", | |
3850 | - boolean_type_node, fn_start); | |
3851 | 3880 | tree resume_idx_name |
3852 | 3881 | = coro_make_frame_entry (&field_list, "__resume_at", |
3853 | 3882 | short_unsigned_type_node, fn_start); |
@@ -3938,11 +3967,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
3938 | 3967 | cp_walk_tree (&fnbody, register_param_uses, ¶m_data, NULL); |
3939 | 3968 | } |
3940 | 3969 | |
3941 | - /* Initial suspend is mandated. */ | |
3942 | - tree init_susp_name = coro_make_frame_entry (&field_list, "__aw_s.is", | |
3943 | - initial_suspend_type, fn_start); | |
3944 | - | |
3945 | - register_await_info (initial_await, initial_suspend_type, init_susp_name); | |
3970 | + /* We need to know, and inspect, each suspend point in the function | |
3971 | + in several places. It's convenient to place this map out of line | |
3972 | + since it's used from tree walk callbacks. */ | |
3973 | + suspend_points = new hash_map<tree, suspend_point_info>; | |
3946 | 3974 | |
3947 | 3975 | /* Now insert the data for any body await points, at this time we also need |
3948 | 3976 | to promote any temporaries that are captured by reference (to regular |
@@ -3955,12 +3983,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
3955 | 3983 | body_aw_points.to_replace = make_tree_vector (); |
3956 | 3984 | cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL); |
3957 | 3985 | |
3958 | - /* Final suspend is mandated. */ | |
3959 | - tree fin_susp_name = coro_make_frame_entry (&field_list, "__aw_s.fs", | |
3960 | - final_suspend_type, fn_start); | |
3961 | - | |
3962 | - register_await_info (final_await, final_suspend_type, fin_susp_name); | |
3963 | - | |
3964 | 3986 | /* 4. Now make space for local vars, this is conservative again, and we |
3965 | 3987 | would expect to delete unused entries later. */ |
3966 | 3988 | hash_map<tree, local_var_info> local_var_uses; |
@@ -4465,17 +4487,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
4465 | 4487 | r = coro_build_cvt_void_expr_stmt (r, fn_start); |
4466 | 4488 | add_stmt (r); |
4467 | 4489 | |
4468 | - /* Initialize 'initial-await-resume-called' as per | |
4469 | - [dcl.fct.def.coroutine] / 5.3 */ | |
4470 | - tree i_a_r_c_m | |
4471 | - = lookup_member (coro_frame_type, iarc_name, 1, 0, tf_warning_or_error); | |
4472 | - tree i_a_r_c = build_class_member_access_expr (deref_fp, i_a_r_c_m, | |
4473 | - NULL_TREE, false, | |
4474 | - tf_warning_or_error); | |
4475 | - r = build2 (INIT_EXPR, boolean_type_node, i_a_r_c, boolean_false_node); | |
4476 | - r = coro_build_cvt_void_expr_stmt (r, fn_start); | |
4477 | - add_stmt (r); | |
4478 | - | |
4479 | 4490 | /* So .. call the actor .. */ |
4480 | 4491 | r = build_call_expr_loc (fn_start, actor, 1, coro_fp); |
4481 | 4492 | r = maybe_cleanup_point_expr_void (r); |
@@ -4545,133 +4556,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
4545 | 4556 | BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); |
4546 | 4557 | TREE_SIDE_EFFECTS (ramp_bind) = true; |
4547 | 4558 | |
4548 | - /* We know the "real" promise and have a frame layout with a slot for each | |
4549 | - suspend point, so we can build an actor function (which contains the | |
4550 | - functionality for both 'resume' and 'destroy'). | |
4551 | - | |
4552 | - wrap the function body in a try {} catch (...) {} block, if exceptions | |
4553 | - are enabled. */ | |
4554 | - | |
4555 | - /* First make a new block for the body - that will be embedded in the | |
4556 | - re-written function. */ | |
4557 | - tree first = expr_first (fnbody); | |
4558 | - bool orig_fn_has_outer_bind = false; | |
4559 | - tree replace_blk = NULL_TREE; | |
4560 | - if (first && TREE_CODE (first) == BIND_EXPR) | |
4561 | - { | |
4562 | - orig_fn_has_outer_bind = true; | |
4563 | - tree block = BIND_EXPR_BLOCK (first); | |
4564 | - replace_blk = make_node (BLOCK); | |
4565 | - if (block) /* missing block is probably an error. */ | |
4566 | - { | |
4567 | - gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
4568 | - gcc_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
4569 | - BLOCK_VARS (replace_blk) = BLOCK_VARS (block); | |
4570 | - BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block); | |
4571 | - for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b)) | |
4572 | - BLOCK_SUPERCONTEXT (b) = replace_blk; | |
4573 | - } | |
4574 | - BIND_EXPR_BLOCK (first) = replace_blk; | |
4575 | - } | |
4576 | - | |
4577 | - /* actor's version of the promise. */ | |
4578 | - tree actor_frame = build1_loc (fn_start, INDIRECT_REF, coro_frame_type, | |
4579 | - DECL_ARGUMENTS (actor)); | |
4580 | - tree ap_m = lookup_member (coro_frame_type, get_identifier ("__p"), 1, 0, | |
4581 | - tf_warning_or_error); | |
4582 | - tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, | |
4583 | - false, tf_warning_or_error); | |
4584 | - | |
4585 | - /* Now we've built the promise etc, process fnbody for co_returns. | |
4586 | - We want the call to return_void () below and it has no params so | |
4587 | - we can create it once here. | |
4588 | - Calls to return_value () will have to be checked and created as | |
4589 | - required. */ | |
4590 | - | |
4591 | - tree return_void | |
4592 | - = coro_build_promise_expression (current_function_decl, ap, | |
4593 | - coro_return_void_identifier, | |
4594 | - fn_start, NULL, /*musthave=*/false); | |
4595 | - | |
4596 | - /* [stmt.return.coroutine] (2.2 : 3) if p.return_void() is a valid | |
4597 | - expression, flowing off the end of a coroutine is equivalent to | |
4598 | - co_return; otherwise UB. | |
4599 | - We just inject the call to p.return_void() here, and fall through to | |
4600 | - the final_suspend: label (eliding the goto). If the function body has | |
4601 | - a co_return, then this statement will be unreachable and DCEd. */ | |
4602 | - if (return_void != NULL_TREE) | |
4603 | - { | |
4604 | - tree append = push_stmt_list (); | |
4605 | - add_stmt (fnbody); | |
4606 | - add_stmt (return_void); | |
4607 | - fnbody = pop_stmt_list(append); | |
4608 | - } | |
4609 | - | |
4610 | - if (flag_exceptions) | |
4611 | - { | |
4612 | - tree ueh | |
4613 | - = coro_build_promise_expression (current_function_decl, ap, | |
4614 | - coro_unhandled_exception_identifier, | |
4615 | - fn_start, NULL, /*musthave=*/true); | |
4616 | - | |
4617 | - /* The try block is just the original function, there's no real | |
4618 | - need to call any function to do this. */ | |
4619 | - fnbody = build_stmt (fn_start, TRY_BLOCK, fnbody, NULL_TREE); | |
4620 | - TRY_HANDLERS (fnbody) = push_stmt_list (); | |
4621 | - /* Mimic what the parser does for the catch. */ | |
4622 | - tree handler = begin_handler (); | |
4623 | - finish_handler_parms (NULL_TREE, handler); /* catch (...) */ | |
4624 | - | |
4625 | - /* Get the initial await resume called value. */ | |
4626 | - tree i_a_r_c = build_class_member_access_expr (actor_frame, i_a_r_c_m, | |
4627 | - NULL_TREE, false, | |
4628 | - tf_warning_or_error); | |
4629 | - tree not_iarc_if = begin_if_stmt (); | |
4630 | - tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, | |
4631 | - boolean_type_node, i_a_r_c); | |
4632 | - finish_if_stmt_cond (not_iarc, not_iarc_if); | |
4633 | - /* If the initial await resume called value is false, rethrow... */ | |
4634 | - tree rethrow = build_throw (fn_start, NULL_TREE); | |
4635 | - TREE_NO_WARNING (rethrow) = true; | |
4636 | - finish_expr_stmt (rethrow); | |
4637 | - finish_then_clause (not_iarc_if); | |
4638 | - tree iarc_scope = IF_SCOPE (not_iarc_if); | |
4639 | - IF_SCOPE (not_iarc_if) = NULL; | |
4640 | - not_iarc_if = do_poplevel (iarc_scope); | |
4641 | - add_stmt (not_iarc_if); | |
4642 | - /* ... else call the promise unhandled exception method. */ | |
4643 | - ueh = maybe_cleanup_point_expr_void (ueh); | |
4644 | - add_stmt (ueh); | |
4645 | - finish_handler (handler); | |
4646 | - TRY_HANDLERS (fnbody) = pop_stmt_list (TRY_HANDLERS (fnbody)); | |
4647 | - /* If the function starts with a BIND_EXPR, then we need to create | |
4648 | - one here to contain the try-catch and to link up the scopes. */ | |
4649 | - if (orig_fn_has_outer_bind) | |
4650 | - { | |
4651 | - fnbody = build3 (BIND_EXPR, void_type_node, NULL, fnbody, NULL); | |
4652 | - /* Make and connect the scope blocks. */ | |
4653 | - tree tcb_block = make_node (BLOCK); | |
4654 | - /* .. and connect it here. */ | |
4655 | - BLOCK_SUPERCONTEXT (replace_blk) = tcb_block; | |
4656 | - BLOCK_SUBBLOCKS (tcb_block) = replace_blk; | |
4657 | - BIND_EXPR_BLOCK (fnbody) = tcb_block; | |
4658 | - TREE_SIDE_EFFECTS (fnbody) = true; | |
4659 | - } | |
4660 | - } | |
4661 | - else if (pedantic) | |
4662 | - { | |
4663 | - /* We still try to look for the promise method and warn if it's not | |
4664 | - present. */ | |
4665 | - tree ueh_meth | |
4666 | - = lookup_promise_method (orig, coro_unhandled_exception_identifier, | |
4667 | - fn_start, /*musthave=*/false); | |
4668 | - if (!ueh_meth || ueh_meth == error_mark_node) | |
4669 | - warning_at (fn_start, 0, "no member named %qE in %qT", | |
4670 | - coro_unhandled_exception_identifier, | |
4671 | - get_coroutine_promise_type (orig)); | |
4672 | - } | |
4673 | - /* Else we don't check and don't care if the method is missing. */ | |
4674 | - | |
4675 | 4559 | /* Start to build the final functions. |
4676 | 4560 | |
4677 | 4561 | We push_deferring_access_checks to avoid these routines being seen as |
@@ -4681,7 +4565,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) | ||
4681 | 4565 | |
4682 | 4566 | /* Build the actor... */ |
4683 | 4567 | build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses, |
4684 | - &local_var_uses, param_dtor_list, initial_await, final_await, | |
4568 | + &local_var_uses, param_dtor_list, fs_label, resume_fn_field, | |
4685 | 4569 | body_aw_points.await_number, frame_size); |
4686 | 4570 | |
4687 | 4571 | /* Destroyer ... */ |