LCOV - code coverage report
Current view: top level - gcc - coroutine-passes.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 88.2 % 211 186
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* coroutine expansion and optimisation passes.
       2              : 
       3              :    Copyright (C) 2018-2026 Free Software Foundation, Inc.
       4              : 
       5              :  Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
       6              : 
       7              : This file is part of GCC.
       8              : 
       9              : GCC is free software; you can redistribute it and/or modify it under
      10              : the terms of the GNU General Public License as published by the Free
      11              : Software Foundation; either version 3, or (at your option) any later
      12              : version.
      13              : 
      14              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      15              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      16              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      17              : for more details.
      18              : 
      19              : You should have received a copy of the GNU General Public License
      20              : along with GCC; see the file COPYING3.  If not see
      21              : <http://www.gnu.org/licenses/>.  */
      22              : 
      23              : #include "config.h"
      24              : #include "system.h"
      25              : #include "coretypes.h"
      26              : #include "backend.h"
      27              : #include "target.h"
      28              : #include "tree.h"
      29              : #include "gimple.h"
      30              : #include "tree-pass.h"
      31              : #include "ssa.h"
      32              : #include "calls.h"
      33              : #include "cgraph.h"
      34              : #include "pretty-print.h"
      35              : #include "diagnostic-core.h"
      36              : #include "fold-const.h"
      37              : #include "internal-fn.h"
      38              : #include "langhooks.h"
      39              : #include "gimplify.h"
      40              : #include "gimple-iterator.h"
      41              : #include "gimplify-me.h"
      42              : #include "gimple-walk.h"
      43              : #include "gimple-fold.h"
      44              : #include "tree-cfg.h"
      45              : #include "tree-into-ssa.h"
      46              : #include "tree-ssa-propagate.h"
      47              : #include "gimple-pretty-print.h"
      48              : #include "cfghooks.h"
      49              : 
      50              : /* Here we:
      51              :    * lower the internal function that implements an exit from scope.
      52              :    * expand the builtins that are used to implement the library
      53              :      interfaces to the coroutine frame.  */
      54              : 
      55              : static tree
      56     26226555 : lower_coro_builtin (gimple_stmt_iterator *gsi, bool *handled_ops_p,
      57              :                     struct walk_stmt_info *wi ATTRIBUTE_UNUSED)
      58              : {
      59     26226555 :   gimple *stmt = gsi_stmt (*gsi);
      60     26226555 :   *handled_ops_p = !gimple_has_substatements (stmt);
      61              : 
      62     26226555 :   if (gimple_code (stmt) != GIMPLE_CALL)
      63              :     return NULL_TREE;
      64              : 
      65              :   /* This internal function implements an exit from scope without
      66              :      performing any cleanups; it jumps directly to the label provided.  */
      67      4894985 :   if (gimple_call_internal_p (stmt)
      68      4894985 :       && gimple_call_internal_fn (stmt) == IFN_CO_SUSPN)
      69              :     {
      70         4102 :       tree dest = TREE_OPERAND (gimple_call_arg (stmt, 0), 0);
      71         4102 :       ggoto *g = gimple_build_goto (dest);
      72         4102 :       gsi_replace (gsi, g, /* do EH */ false);
      73         4102 :       *handled_ops_p = true;
      74         4102 :       return NULL_TREE;
      75              :     }
      76              : 
      77      4890883 :   tree decl = gimple_call_fndecl (stmt);
      78      4890883 :   if (!decl || !fndecl_built_in_p (decl, BUILT_IN_NORMAL))
      79              :     return NULL_TREE;
      80              : 
      81              :   /* The remaining builtins implement the library interfaces to the coro
      82              :      frame.  */
      83       791283 :   unsigned call_idx = 0;
      84              : 
      85       791283 :   switch (DECL_FUNCTION_CODE (decl))
      86              :     {
      87              :     default:
      88              :       break;
      89         1789 :     case BUILT_IN_CORO_PROMISE:
      90         1789 :       {
      91              :         /* If we are discarding this, then skip it; the function has no
      92              :            side-effects.  */
      93         1789 :         tree lhs = gimple_call_lhs (stmt);
      94         1789 :         if (!lhs)
      95              :           {
      96            0 :             gsi_remove (gsi, true);
      97            0 :             *handled_ops_p = true;
      98            0 :             return NULL_TREE;
      99              :           }
     100              :         /* The coro frame starts with two pointers (to the resume and
     101              :            destroy() functions).  These are followed by the promise which
     102              :            is aligned as per type [or user attribute].
     103              :            The input pointer is the first argument.
     104              :            The promise alignment is the second and the third is a bool
     105              :            that is true when we are converting from a promise ptr to a
     106              :            frame pointer, and false for the inverse.  */
     107         1789 :         tree ptr = gimple_call_arg (stmt, 0);
     108         1789 :         tree align_t = gimple_call_arg (stmt, 1);
     109         1789 :         tree from = gimple_call_arg (stmt, 2);
     110         1789 :         gcc_checking_assert (TREE_CODE (align_t) == INTEGER_CST);
     111         1789 :         gcc_checking_assert (TREE_CODE (from) == INTEGER_CST);
     112         1789 :         bool dir = wi::to_wide (from) != 0;
     113         1789 :         HOST_WIDE_INT promise_align = TREE_INT_CST_LOW (align_t);
     114         1789 :         HOST_WIDE_INT psize =
     115         1789 :           TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
     116         1789 :         HOST_WIDE_INT align = TYPE_ALIGN_UNIT (ptr_type_node);
     117         1789 :         align = MAX (align, promise_align);
     118         1789 :         psize *= 2; /* Start with two pointers.  */
     119         1789 :         psize = ROUND_UP (psize, align);
     120         1789 :         HOST_WIDE_INT offs = dir ? -psize : psize;
     121         1789 :         tree repl = build2 (POINTER_PLUS_EXPR, ptr_type_node, ptr,
     122         1789 :                             size_int (offs));
     123         1789 :         gassign *grpl = gimple_build_assign (lhs, repl);
     124         1789 :         gsi_replace (gsi, grpl, true);
     125         1789 :         *handled_ops_p = true;
     126              :       }
     127         1789 :       break;
     128          896 :     case BUILT_IN_CORO_DESTROY:
     129          896 :       call_idx = 1;
     130              :       /* FALLTHROUGH */
     131         3202 :     case BUILT_IN_CORO_RESUME:
     132         3202 :       {
     133         3202 :         tree ptr = gimple_call_arg (stmt, 0); /* frame ptr.  */
     134         3202 :         HOST_WIDE_INT psize =
     135         3202 :           TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
     136         3202 :         HOST_WIDE_INT offset = call_idx * psize;
     137         3202 :         tree fntype = TREE_TYPE (decl);
     138         3202 :         tree fntype_ptr = build_pointer_type (fntype);
     139         3202 :         tree fntype_ppp = build_pointer_type (fntype_ptr);
     140         3202 :         tree indirect = fold_build2 (MEM_REF, fntype_ptr, ptr,
     141              :                                      build_int_cst (fntype_ppp, offset));
     142         3202 :         tree f_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (fntype_ptr));
     143         3202 :         gassign *get_fptr = gimple_build_assign (f_ptr_tmp, indirect);
     144         3202 :         gsi_insert_before (gsi, get_fptr, GSI_SAME_STMT);
     145         3202 :         gimple_call_set_fn (static_cast<gcall *> (stmt), f_ptr_tmp);
     146         3202 :         *handled_ops_p = true;
     147              :       }
     148         3202 :       break;
     149          810 :     case BUILT_IN_CORO_DONE:
     150          810 :       {
     151              :         /* If we are discarding this, then skip it; the function has no
     152              :            side-effects.  */
     153          810 :         tree lhs = gimple_call_lhs (stmt);
     154          810 :         if (!lhs)
     155              :           {
     156            0 :             gsi_remove (gsi, true);
     157            0 :             *handled_ops_p = true;
     158            0 :             return NULL_TREE;
     159              :           }
     160              :         /* When we're done, the resume fn is set to NULL.  */
     161          810 :         tree ptr = gimple_call_arg (stmt, 0); /* frame ptr.  */
     162          810 :         tree vpp = build_pointer_type (ptr_type_node);
     163          810 :         tree indirect
     164          810 :           = fold_build2 (MEM_REF, vpp, ptr, build_int_cst (vpp, 0));
     165          810 :         tree d_ptr_tmp = make_ssa_name (ptr_type_node);
     166          810 :         gassign *get_dptr = gimple_build_assign (d_ptr_tmp, indirect);
     167          810 :         gsi_insert_before (gsi, get_dptr, GSI_SAME_STMT);
     168          810 :         tree done = fold_build2 (EQ_EXPR, boolean_type_node, d_ptr_tmp,
     169              :                                  null_pointer_node);
     170          810 :         gassign *get_res = gimple_build_assign (lhs, done);
     171          810 :         gsi_replace (gsi, get_res, true);
     172          810 :         *handled_ops_p = true;
     173              :       }
     174          810 :       break;
     175              :     }
     176              :   return NULL_TREE;
     177              : }
     178              : 
     179              : /* Main entry point for lowering coroutine FE builtins.  */
     180              : 
     181              : static unsigned int
     182      1580669 : execute_lower_coro_builtins (void)
     183              : {
     184      1580669 :   struct walk_stmt_info wi;
     185      1580669 :   gimple_seq body;
     186              : 
     187      1580669 :   body = gimple_body (current_function_decl);
     188      1580669 :   memset (&wi, 0, sizeof (wi));
     189      1580669 :   walk_gimple_seq_mod (&body, lower_coro_builtin, NULL, &wi);
     190      1580669 :   gimple_set_body (current_function_decl, body);
     191              : 
     192      1580669 :   return 0;
     193              : }
     194              : 
     195              : namespace {
     196              : 
     197              : const pass_data pass_data_coroutine_lower_builtins = {
     198              :   GIMPLE_PASS,           /* type */
     199              :   "coro-lower-builtins", /* name */
     200              :   OPTGROUP_NONE,         /* optinfo_flags */
     201              :   TV_NONE,               /* tv_id */
     202              :   0,                     /* properties_required */
     203              :   0,                     /* properties_provided */
     204              :   0,                     /* properties_destroyed */
     205              :   0,                     /* todo_flags_start */
     206              :   0                      /* todo_flags_finish */
     207              : };
     208              : 
     209              : class pass_coroutine_lower_builtins : public gimple_opt_pass
     210              : {
     211              : public:
     212       285722 :   pass_coroutine_lower_builtins (gcc::context *ctxt)
     213       571444 :     : gimple_opt_pass (pass_data_coroutine_lower_builtins, ctxt)
     214              :   {}
     215              : 
     216              :   /* opt_pass methods: */
     217      2869205 :   bool gate (function *) final override { return flag_coroutines; };
     218              : 
     219      1580669 :   unsigned int execute (function *f ATTRIBUTE_UNUSED) final override
     220              :   {
     221      1580669 :     return execute_lower_coro_builtins ();
     222              :   }
     223              : 
     224              : }; // class pass_coroutine_lower_builtins
     225              : 
     226              : } // namespace
     227              : 
     228              : gimple_opt_pass *
     229       285722 : make_pass_coroutine_lower_builtins (gcc::context *ctxt)
     230              : {
     231       285722 :   return new pass_coroutine_lower_builtins (ctxt);
     232              : }
     233              : 
     234              : /* Expand the remaining coroutine IFNs.
     235              : 
     236              :    In the front end we construct a single actor function that contains
     237              :    the coroutine state machine.
     238              : 
     239              :    The actor function has three entry conditions:
     240              :     1. from the ramp, resume point 0 - to initial-suspend.
     241              :     2. when resume () is executed (resume point N).
     242              :     3. from the destroy () shim when that is executed.
     243              : 
     244              :    The actor function begins with two dispatchers; one for resume and
     245              :    one for destroy (where the initial entry from the ramp is a special-
     246              :    case of resume point 0).
     247              : 
     248              :    Each suspend point and each dispatch entry is marked with an IFN such
     249              :    that we can connect the relevant dispatchers to their target labels.
     250              : 
     251              :    So, if we have:
     252              : 
     253              :    CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR)
     254              : 
     255              :    This is await point NUM, and is the final await if FINAL is non-zero.
     256              :    The resume point is RES_LAB, and the destroy point is DEST_LAB.
     257              : 
     258              :    We expect to find a CO_ACTOR (NUM) in the resume dispatcher and a
     259              :    CO_ACTOR (NUM+1) in the destroy dispatcher.
     260              : 
     261              :    Initially, the intent of keeping the resume and destroy paths together
     262              :    is that the conditionals controlling them are identical, and thus there
     263              :    would be duplication of any optimisation of those paths if the split
     264              :    were earlier.
     265              : 
     266              :    Subsequent inlining of the actor (and DCE) is then able to extract the
     267              :    resume and destroy paths as separate functions if that is found
     268              :    profitable by the optimisers.
     269              : 
     270              :    Once we have remade the connections to their correct postions, we elide
     271              :    the labels that the front end inserted.  */
     272              : 
     273              : static void
     274         8134 : move_edge_and_update (edge e, basic_block old_bb, basic_block new_bb)
     275              : {
     276         8134 :   if (dump_file)
     277            0 :     fprintf (dump_file, "redirecting edge from bb %u to bb %u\n", old_bb->index,
     278              :              new_bb->index);
     279              : 
     280         8134 :   e = redirect_edge_and_branch (e, new_bb);
     281         8134 :   if (!e && dump_file)
     282            0 :     fprintf (dump_file, "failed to redirect edge ..  \n");
     283              : 
     284              :   /* Die if we failed.  */
     285         8134 :   gcc_checking_assert (e);
     286         8134 : }
     287              : 
     288              : static unsigned int
     289         4496 : execute_early_expand_coro_ifns (void)
     290              : {
     291              :   /* Don't rebuild stuff unless we have to. */
     292         4496 :   unsigned int todoflags = 0;
     293         4496 :   bool changed = false;
     294              :   /* Some of the possible YIELD points will hopefully have been removed by
     295              :      earlier optimisations; record the ones that are still present.  */
     296         4496 :   hash_map<int_hash<HOST_WIDE_INT, -1, -2>, tree> destinations;
     297              :   /* List of dispatch points to update.  */
     298         4496 :   auto_vec<gimple_stmt_iterator, 16> actor_worklist;
     299         4496 :   basic_block bb;
     300         4496 :   gimple_stmt_iterator gsi;
     301              : 
     302        69485 :   FOR_EACH_BB_FN (bb, cfun)
     303       360427 :     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     304              :       {
     305       230449 :         gimple *stmt = gsi_stmt (gsi);
     306              : 
     307              :         /* Tell the user about 'alloca', we don't support it yet.  */
     308       230449 :         if (gimple_alloca_call_p (stmt))
     309              :           {
     310            6 :             sorry_at (gimple_location (stmt),
     311              :                       "%<alloca%> is not yet supported in coroutines");
     312            6 :             gsi_next (&gsi);
     313            6 :             continue;
     314              :           }
     315              : 
     316       230443 :         if (!is_gimple_call (stmt) || !gimple_call_internal_p (stmt))
     317              :           {
     318       216374 :             gsi_next (&gsi);
     319       216374 :             continue;
     320              :           }
     321        14069 :         switch (gimple_call_internal_fn (stmt))
     322              :           {
     323         1470 :           case IFN_CO_FRAME:
     324         1470 :             {
     325              :               /* This internal function is a placeholder for the frame
     326              :                  size.  In principle, we might lower it later (after some
     327              :                  optimisation had reduced the frame size).  At present,
     328              :                  without any such optimisation, we just set it here.  */
     329         1470 :               tree lhs = gimple_call_lhs (stmt);
     330         1470 :               tree size = gimple_call_arg (stmt, 0);
     331              :               /* Right now, this is a trivial operation - copy through
     332              :                  the size computed during initial layout.  */
     333         1470 :               gassign *grpl = gimple_build_assign (lhs, size);
     334         1470 :               gsi_replace (&gsi, grpl, true);
     335         1470 :               gsi_next (&gsi);
     336              :             }
     337         1470 :             break;
     338         8140 :           case IFN_CO_ACTOR:
     339         8140 :             changed = true;
     340         8140 :             actor_worklist.safe_push (gsi); /* Save for later.  */
     341         8140 :             gsi_next (&gsi);
     342         8140 :             break;
     343         4067 :           case IFN_CO_YIELD:
     344         4067 :             {
     345         4067 :               changed = true;
     346              :               /* .CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR);
     347              :                   NUM = await number.
     348              :                   FINAL = 1 if this is the final_suspend() await.
     349              :                   RES_LAB = resume point label.
     350              :                   DEST_LAB = destroy point label.
     351              :                   FRAME_PTR = is a null pointer with the type of the coro
     352              :                               frame, so that we can resize, if needed.  */
     353         4067 :               if (dump_file)
     354            0 :                 fprintf (dump_file, "saw CO_YIELD in BB %u\n", bb->index);
     355         4067 :               tree num = gimple_call_arg (stmt, 0); /* yield point.  */
     356         4067 :               HOST_WIDE_INT idx = TREE_INT_CST_LOW (num);
     357         4067 :               bool existed;
     358         4067 :               tree res_tgt = TREE_OPERAND (gimple_call_arg (stmt, 2), 0);
     359         4067 :               tree &res_dest = destinations.get_or_insert (idx, &existed);
     360         4067 :               if (existed && dump_file)
     361              :                 {
     362            0 :                   fprintf (
     363              :                     dump_file,
     364              :                     "duplicate YIELD RESUME point (" HOST_WIDE_INT_PRINT_DEC
     365              :                     ") ?\n",
     366              :                     idx);
     367            0 :                   print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
     368              :                 }
     369              :               else
     370         4067 :                 res_dest = res_tgt;
     371         4067 :               tree dst_tgt = TREE_OPERAND (gimple_call_arg (stmt, 3), 0);
     372         4067 :               tree &dst_dest = destinations.get_or_insert (idx + 1, &existed);
     373         4067 :               if (existed && dump_file)
     374              :                 {
     375            0 :                   fprintf (
     376              :                     dump_file,
     377              :                     "duplicate YIELD DESTROY point (" HOST_WIDE_INT_PRINT_DEC
     378              :                     ") ?\n",
     379              :                     idx + 1);
     380            0 :                   print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
     381              :                 }
     382              :               else
     383         4067 :                 dst_dest = dst_tgt;
     384              :               /* lose the co_yield.  */
     385         4067 :               gsi_remove (&gsi, true);
     386         4067 :               stmt = gsi_stmt (gsi); /* next. */
     387              :               /* lose the copy present at O0.  */
     388         4067 :               if (is_gimple_assign (stmt))
     389              :                 {
     390            0 :                   gsi_remove (&gsi, true);
     391            0 :                   stmt = gsi_stmt (gsi);
     392              :                 }
     393              :               /* Simplify the switch or if following.  */
     394         4067 :               if (gswitch *gsw = dyn_cast<gswitch *> (stmt))
     395              :                 {
     396         4067 :                   gimple_switch_set_index (gsw, integer_zero_node);
     397         4067 :                   fold_stmt (&gsi);
     398              :                 }
     399            0 :               else if (gcond *gif = dyn_cast<gcond *> (stmt))
     400              :                 {
     401            0 :                   if (gimple_cond_code (gif) == EQ_EXPR)
     402            0 :                     gimple_cond_make_true (gif);
     403              :                   else
     404            0 :                     gimple_cond_make_false (gif);
     405            0 :                   fold_stmt (&gsi);
     406              :                 }
     407            0 :               else if (dump_file)
     408            0 :                 print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
     409         4067 :               if (gsi_end_p (gsi))
     410              :                 break;
     411         4067 :               continue;
     412         4067 :             }
     413          392 :           default:
     414          392 :             gsi_next (&gsi);
     415          392 :             break;
     416              :           }
     417              :       }
     418              : 
     419         4496 :   if (!changed)
     420              :     {
     421         3015 :       if (dump_file)
     422            0 :         fprintf (dump_file, "coro: nothing to do\n");
     423         3015 :       return todoflags;
     424              :     }
     425              : 
     426         9621 :   while (!actor_worklist.is_empty ())
     427              :     {
     428         8140 :       gsi = actor_worklist.pop ();
     429         8140 :       gimple *stmt = gsi_stmt (gsi);
     430         8140 :       gcc_checking_assert (is_gimple_call (stmt)
     431              :                            && gimple_call_internal_p (stmt)
     432              :                            && gimple_call_internal_fn (stmt) == IFN_CO_ACTOR);
     433         8140 :       bb = gsi_bb (gsi);
     434         8140 :       HOST_WIDE_INT idx = TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
     435         8140 :       tree *seen = destinations.get (idx);
     436         8140 :       changed = true;
     437              : 
     438         8140 :       if (dump_file)
     439            0 :         fprintf (dump_file, "saw CO_ACTOR in BB %u\n", bb->index);
     440              : 
     441         8140 :       if (!seen)
     442              :         {
     443              :           /* If we never saw this index, it means that the CO_YIELD
     444              :           associated was elided during earlier optimisations, so we
     445              :           don't need to fix up the switch targets.  */
     446            6 :           if (dump_file)
     447            0 :             fprintf (dump_file, "yield point " HOST_WIDE_INT_PRINT_DEC
     448              :                      " not used, removing it .. \n",  idx);
     449            6 :           gsi_remove (&gsi, true);
     450            6 :           release_defs (stmt);
     451              :         }
     452              :       else
     453              :         {
     454              :           /* So we need to switch the target of this switch case to the
     455              :              relevant BB.  */
     456         8134 :           basic_block new_bb = label_to_block (cfun, *seen);
     457              :           /* We expect the block we're modifying to contain a single
     458              :              CO_ACTOR() followed by a goto <switch default bb>.  */
     459         8134 :           gcc_checking_assert (EDGE_COUNT (bb->succs) == 1);
     460         8134 :           edge e;
     461         8134 :           edge_iterator ei;
     462        16268 :           FOR_EACH_EDGE (e, ei, bb->succs)
     463              :             {
     464         8134 :               basic_block old_bb = e->dest;
     465         8134 :               move_edge_and_update (e, old_bb, new_bb);
     466              :             }
     467         8134 :           gsi_remove (&gsi, true);
     468              :         }
     469              :     }
     470              : 
     471              :   /* Changed the CFG.  */
     472         4496 :   todoflags |= TODO_cleanup_cfg;
     473              :   return todoflags;
     474         4496 : }
     475              : 
     476              : namespace {
     477              : 
     478              : const pass_data pass_data_coroutine_early_expand_ifns = {
     479              :   GIMPLE_PASS,              /* type */
     480              :   "coro-early-expand-ifns", /* name */
     481              :   OPTGROUP_NONE,            /* optinfo_flags */
     482              :   TV_NONE,                  /* tv_id */
     483              :   (PROP_cfg),               /* properties_required */
     484              :   0,                        /* properties_provided */
     485              :   0,                        /* properties_destroyed */
     486              :   0,                        /* todo_flags_start */
     487              :   0                         /* todo_flags_finish, set this in the fn. */
     488              : };
     489              : 
     490              : class pass_coroutine_early_expand_ifns : public gimple_opt_pass
     491              : {
     492              : public:
     493       285722 :   pass_coroutine_early_expand_ifns (gcc::context *ctxt)
     494       571444 :     : gimple_opt_pass (pass_data_coroutine_early_expand_ifns, ctxt)
     495              :   {}
     496              : 
     497              :   /* opt_pass methods: */
     498      2869205 :   bool gate (function *f) final override
     499              :     {
     500      2869205 :       return flag_coroutines && f->coroutine_component;
     501              :     }
     502              : 
     503         4496 :   unsigned int execute (function *f ATTRIBUTE_UNUSED) final override
     504              :   {
     505         4496 :     return execute_early_expand_coro_ifns ();
     506              :   }
     507              : 
     508              : }; // class pass_coroutine_expand_ifns
     509              : 
     510              : } // namespace
     511              : 
     512              : gimple_opt_pass *
     513       285722 : make_pass_coroutine_early_expand_ifns (gcc::context *ctxt)
     514              : {
     515       285722 :   return new pass_coroutine_early_expand_ifns (ctxt);
     516              : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.