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