]>
Commit | Line | Data |
---|---|---|
0732f75f | 1 | /* SSA Jump Threading |
7adcbafe | 2 | Copyright (C) 2005-2022 Free Software Foundation, Inc. |
0732f75f JL |
3 | |
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GCC; see the file COPYING3. If not see | |
18 | <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "config.h" | |
21 | #include "system.h" | |
22 | #include "coretypes.h" | |
23 | #include "backend.h" | |
24 | #include "predict.h" | |
25 | #include "tree.h" | |
26 | #include "gimple.h" | |
27 | #include "fold-const.h" | |
28 | #include "cfgloop.h" | |
29 | #include "gimple-iterator.h" | |
30 | #include "tree-cfg.h" | |
31 | #include "tree-ssa-threadupdate.h" | |
0732f75f JL |
32 | #include "tree-ssa-loop.h" |
33 | #include "cfganal.h" | |
34 | #include "tree-pass.h" | |
8b2ef235 JL |
35 | #include "gimple-ssa.h" |
36 | #include "tree-phinodes.h" | |
27bddc4a | 37 | #include "tree-inline.h" |
0a4e5cf3 | 38 | #include "tree-vectorizer.h" |
2e96b5f1 AH |
39 | #include "value-range.h" |
40 | #include "gimple-range.h" | |
41 | #include "tree-ssa-threadedge.h" | |
42 | #include "gimple-range-path.h" | |
43 | #include "ssa.h" | |
44 | #include "tree-cfgcleanup.h" | |
779275c0 | 45 | #include "tree-pretty-print.h" |
01b50387 | 46 | #include "cfghooks.h" |
bc5baac5 | 47 | #include "dbgcnt.h" |
0732f75f | 48 | |
69e55442 AH |
49 | // Path registry for the backwards threader. After all paths have been |
50 | // registered with register_path(), thread_through_all_blocks() is called | |
51 | // to modify the CFG. | |
52 | ||
7f6c2258 | 53 | class back_threader_registry : public back_jt_path_registry |
69e55442 AH |
54 | { |
55 | public: | |
69e55442 | 56 | bool register_path (const vec<basic_block> &, edge taken); |
69e55442 AH |
57 | }; |
58 | ||
59 | // Class to abstract the profitability code for the backwards threader. | |
60 | ||
61 | class back_threader_profitability | |
62 | { | |
63 | public: | |
64 | back_threader_profitability (bool speed_p) | |
65 | : m_speed_p (speed_p) | |
66 | { } | |
67 | bool profitable_path_p (const vec<basic_block> &, tree name, edge taken, | |
68 | bool *irreducible_loop = NULL); | |
69e55442 AH |
69 | private: |
70 | const bool m_speed_p; | |
71 | }; | |
72 | ||
4e0f56d7 AH |
73 | // Back threader flags. |
74 | #define BT_NONE 0 | |
75 | // Generate fast code at the expense of code size. | |
76 | #define BT_SPEED 1 | |
77 | // Resolve unknown SSAs on entry to a threading path. If set, use the | |
78 | // ranger. If not, assume all ranges on entry to a path are VARYING. | |
79 | #define BT_RESOLVE 2 | |
80 | ||
2e96b5f1 AH |
81 | class back_threader |
82 | { | |
2e96b5f1 | 83 | public: |
bc5baac5 | 84 | back_threader (function *fun, unsigned flags, bool first); |
4e0f56d7 AH |
85 | ~back_threader (); |
86 | unsigned thread_blocks (); | |
2e96b5f1 | 87 | private: |
4e0f56d7 | 88 | void maybe_thread_block (basic_block bb); |
34cd97ff | 89 | void find_paths (basic_block bb, tree name); |
bc5baac5 | 90 | bool debug_counter (); |
cbeeadff | 91 | edge maybe_register_path (); |
53080c5b | 92 | void maybe_register_path_dump (edge taken_edge); |
98b212c1 | 93 | void find_paths_to_names (basic_block bb, bitmap imports); |
2e96b5f1 AH |
94 | edge find_taken_edge (const vec<basic_block> &path); |
95 | edge find_taken_edge_cond (const vec<basic_block> &path, gcond *); | |
96 | edge find_taken_edge_switch (const vec<basic_block> &path, gswitch *); | |
779275c0 AH |
97 | virtual void debug (); |
98 | virtual void dump (FILE *out); | |
2e96b5f1 | 99 | |
34cd97ff AH |
100 | back_threader_registry m_registry; |
101 | back_threader_profitability m_profit; | |
4e0f56d7 | 102 | path_range_query *m_solver; |
2e96b5f1 AH |
103 | |
104 | // Current path being analyzed. | |
105 | auto_vec<basic_block> m_path; | |
106 | // Hash to mark visited BBs while analyzing a path. | |
107 | hash_set<basic_block> m_visited_bbs; | |
108 | // The set of SSA names, any of which could potentially change the | |
109 | // value of the final conditional in a path. | |
d71e1be7 | 110 | auto_bitmap m_imports; |
2e96b5f1 AH |
111 | // The last statement in the path. |
112 | gimple *m_last_stmt; | |
113 | // This is a bit of a wart. It's used to pass the LHS SSA name to | |
114 | // the profitability engine. | |
115 | tree m_name; | |
116 | // Marker to differentiate unreachable edges. | |
117 | static const edge UNREACHABLE_EDGE; | |
401aaa59 AH |
118 | // Set to TRUE if unknown SSA names along a path should be resolved |
119 | // with the ranger. Otherwise, unknown SSA names are assumed to be | |
5d4d64fa | 120 | // VARYING. Setting to true is more precise but slower. |
4e0f56d7 AH |
121 | function *m_fun; |
122 | unsigned m_flags; | |
bc5baac5 AH |
123 | // Set to TRUE for the first of each thread[12] pass or the first of |
124 | // each threadfull[12] pass. This is used to differentiate between | |
125 | // the different threading passes so we can set up debug counters. | |
126 | bool m_first; | |
2e96b5f1 AH |
127 | }; |
128 | ||
129 | // Used to differentiate unreachable edges, so we may stop the search | |
130 | // in a the given direction. | |
131 | const edge back_threader::UNREACHABLE_EDGE = (edge) -1; | |
132 | ||
bc5baac5 AH |
133 | back_threader::back_threader (function *fun, unsigned flags, bool first) |
134 | : m_profit (flags & BT_SPEED), | |
135 | m_first (first) | |
2e96b5f1 | 136 | { |
4e0f56d7 AH |
137 | if (flags & BT_SPEED) |
138 | loop_optimizer_init (LOOPS_HAVE_PREHEADERS | LOOPS_HAVE_SIMPLE_LATCHES); | |
139 | else | |
140 | loop_optimizer_init (AVOID_CFG_MODIFICATIONS); | |
141 | ||
142 | m_fun = fun; | |
143 | m_flags = flags; | |
2e96b5f1 | 144 | m_last_stmt = NULL; |
eb5ee646 AH |
145 | |
146 | // The path solver needs EDGE_DFS_BACK in resolving mode. | |
147 | if (flags & BT_RESOLVE) | |
148 | mark_dfs_back_edges (); | |
83ad3a96 | 149 | m_solver = new path_range_query (flags & BT_RESOLVE); |
4e0f56d7 AH |
150 | } |
151 | ||
152 | back_threader::~back_threader () | |
153 | { | |
154 | delete m_solver; | |
4e0f56d7 AH |
155 | |
156 | loop_optimizer_finalize (); | |
2e96b5f1 AH |
157 | } |
158 | ||
bc5baac5 AH |
159 | // A wrapper for the various debug counters for the threading passes. |
160 | // Returns TRUE if it's OK to register the current threading | |
161 | // candidate. | |
162 | ||
163 | bool | |
164 | back_threader::debug_counter () | |
165 | { | |
166 | // The ethread pass is mostly harmless ;-). | |
167 | if ((m_flags & BT_SPEED) == 0) | |
168 | return true; | |
169 | ||
170 | if (m_flags & BT_RESOLVE) | |
171 | { | |
172 | if (m_first && !dbg_cnt (back_threadfull1)) | |
173 | return false; | |
174 | ||
175 | if (!m_first && !dbg_cnt (back_threadfull2)) | |
176 | return false; | |
177 | } | |
178 | else | |
179 | { | |
180 | if (m_first && !dbg_cnt (back_thread1)) | |
181 | return false; | |
182 | ||
183 | if (!m_first && !dbg_cnt (back_thread2)) | |
184 | return false; | |
185 | } | |
186 | return true; | |
187 | } | |
188 | ||
a2ab1a5a AH |
189 | static void |
190 | dump_path (FILE *dump_file, const vec<basic_block> &path) | |
191 | { | |
192 | for (unsigned i = path.length (); i > 0; --i) | |
193 | { | |
194 | basic_block bb = path[i - 1]; | |
195 | fprintf (dump_file, "%d", bb->index); | |
196 | if (i > 1) | |
197 | fprintf (dump_file, "->"); | |
198 | } | |
199 | } | |
200 | ||
53080c5b AH |
201 | // Dump details of an attempt to register a path. |
202 | ||
203 | void | |
204 | back_threader::maybe_register_path_dump (edge taken) | |
205 | { | |
206 | if (m_path.is_empty ()) | |
207 | return; | |
208 | ||
209 | fprintf (dump_file, "path: "); | |
a2ab1a5a | 210 | dump_path (dump_file, m_path); |
53080c5b AH |
211 | fprintf (dump_file, "->"); |
212 | ||
213 | if (taken == UNREACHABLE_EDGE) | |
214 | fprintf (dump_file, "xx REJECTED (unreachable)\n"); | |
215 | else if (taken) | |
216 | fprintf (dump_file, "%d SUCCESS\n", taken->dest->index); | |
217 | else | |
218 | fprintf (dump_file, "xx REJECTED\n"); | |
219 | } | |
220 | ||
221 | // If an outgoing edge can be determined out of the current path, | |
222 | // register it for jump threading and return the taken edge. | |
cbeeadff | 223 | // |
2b59cf47 AH |
224 | // Return NULL if it is unprofitable to thread this path, or the |
225 | // outgoing edge is unknown. Return UNREACHABLE_EDGE if the path is | |
226 | // unreachable. | |
2e96b5f1 | 227 | |
cbeeadff AH |
228 | edge |
229 | back_threader::maybe_register_path () | |
2e96b5f1 | 230 | { |
cbeeadff | 231 | edge taken_edge = find_taken_edge (m_path); |
2e96b5f1 | 232 | |
cbeeadff | 233 | if (taken_edge && taken_edge != UNREACHABLE_EDGE) |
2e96b5f1 | 234 | { |
e53fbb17 | 235 | if (m_visited_bbs.contains (taken_edge->dest)) |
cbeeadff | 236 | { |
2b59cf47 AH |
237 | // Avoid circular paths by indicating there is nothing to |
238 | // see in this direction. | |
239 | taken_edge = UNREACHABLE_EDGE; | |
240 | } | |
241 | else | |
242 | { | |
243 | bool irreducible = false; | |
244 | if (m_profit.profitable_path_p (m_path, m_name, taken_edge, | |
245 | &irreducible) | |
246 | && debug_counter ()) | |
247 | { | |
248 | m_registry.register_path (m_path, taken_edge); | |
249 | ||
250 | if (irreducible) | |
251 | vect_free_loop_info_assumptions (m_path[0]->loop_father); | |
252 | } | |
253 | else | |
254 | taken_edge = NULL; | |
cbeeadff | 255 | } |
2e96b5f1 | 256 | } |
53080c5b AH |
257 | |
258 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
259 | maybe_register_path_dump (taken_edge); | |
260 | ||
cbeeadff | 261 | return taken_edge; |
2e96b5f1 AH |
262 | } |
263 | ||
264 | // Return the known taken edge out of a path. If the path can be | |
265 | // determined to be unreachable, return UNREACHABLE_EDGE. If no | |
266 | // outgoing edge can be calculated, return NULL. | |
267 | ||
268 | edge | |
269 | back_threader::find_taken_edge (const vec<basic_block> &path) | |
270 | { | |
271 | gcc_checking_assert (path.length () > 1); | |
272 | switch (gimple_code (m_last_stmt)) | |
273 | { | |
274 | case GIMPLE_COND: | |
275 | return find_taken_edge_cond (path, as_a<gcond *> (m_last_stmt)); | |
276 | ||
277 | case GIMPLE_SWITCH: | |
278 | return find_taken_edge_switch (path, as_a<gswitch *> (m_last_stmt)); | |
279 | ||
280 | default: | |
281 | return NULL; | |
282 | } | |
283 | } | |
284 | ||
285 | // Same as find_taken_edge, but for paths ending in a switch. | |
286 | ||
287 | edge | |
288 | back_threader::find_taken_edge_switch (const vec<basic_block> &path, | |
289 | gswitch *sw) | |
290 | { | |
291 | tree name = gimple_switch_index (sw); | |
292 | int_range_max r; | |
293 | ||
4e0f56d7 AH |
294 | m_solver->compute_ranges (path, m_imports); |
295 | m_solver->range_of_expr (r, name, sw); | |
2e96b5f1 AH |
296 | |
297 | if (r.undefined_p ()) | |
298 | return UNREACHABLE_EDGE; | |
299 | ||
300 | if (r.varying_p ()) | |
301 | return NULL; | |
302 | ||
113dab2b AH |
303 | tree label = find_case_label_range (sw, &r); |
304 | if (!label) | |
305 | return NULL; | |
2e96b5f1 | 306 | |
113dab2b | 307 | return find_edge (gimple_bb (sw), label_to_block (cfun, CASE_LABEL (label))); |
2e96b5f1 AH |
308 | } |
309 | ||
310 | // Same as find_taken_edge, but for paths ending in a GIMPLE_COND. | |
311 | ||
312 | edge | |
313 | back_threader::find_taken_edge_cond (const vec<basic_block> &path, | |
314 | gcond *cond) | |
315 | { | |
2e96b5f1 | 316 | int_range_max r; |
2e96b5f1 | 317 | |
4e0f56d7 AH |
318 | m_solver->compute_ranges (path, m_imports); |
319 | m_solver->range_of_stmt (r, cond); | |
2e96b5f1 | 320 | |
4e0f56d7 | 321 | if (m_solver->unreachable_path_p ()) |
90ef1535 AH |
322 | return UNREACHABLE_EDGE; |
323 | ||
2e96b5f1 AH |
324 | int_range<2> true_range (boolean_true_node, boolean_true_node); |
325 | int_range<2> false_range (boolean_false_node, boolean_false_node); | |
326 | ||
327 | if (r == true_range || r == false_range) | |
328 | { | |
329 | edge e_true, e_false; | |
330 | basic_block bb = gimple_bb (cond); | |
331 | extract_true_false_edges_from_block (bb, &e_true, &e_false); | |
332 | return r == true_range ? e_true : e_false; | |
333 | } | |
334 | return NULL; | |
335 | } | |
336 | ||
2e96b5f1 AH |
337 | // Find jump threading paths to any of the SSA names in the |
338 | // INTERESTING bitmap, and register any such paths. | |
339 | // | |
2e96b5f1 AH |
340 | // BB is the current path being processed. |
341 | ||
98b212c1 | 342 | void |
2e96b5f1 AH |
343 | back_threader::find_paths_to_names (basic_block bb, bitmap interesting) |
344 | { | |
345 | if (m_visited_bbs.add (bb)) | |
98b212c1 | 346 | return; |
2e96b5f1 AH |
347 | |
348 | m_path.safe_push (bb); | |
349 | ||
f6d01233 | 350 | // Try to resolve the path without looking back. |
98b212c1 AH |
351 | if (m_path.length () > 1 |
352 | && (!m_profit.profitable_path_p (m_path, m_name, NULL) | |
353 | || maybe_register_path ())) | |
d86d81a4 | 354 | ; |
401aaa59 | 355 | |
d86d81a4 RB |
356 | // Continue looking for ways to extend the path |
357 | else | |
2e96b5f1 | 358 | { |
d86d81a4 RB |
359 | // For further greedy searching we want to remove interesting |
360 | // names defined in BB but add ones on the PHI edges for the | |
361 | // respective edges. We do this by starting with all names | |
362 | // not defined in BB as interesting, collecting a list of | |
363 | // interesting PHIs in BB on the fly. Then we iterate over | |
364 | // predecessor edges, adding interesting PHI edge defs to | |
365 | // the set of interesting names to consider when processing it. | |
366 | auto_bitmap new_interesting; | |
367 | auto_vec<gphi *, 4> interesting_phis; | |
368 | bitmap_iterator bi; | |
369 | unsigned i; | |
370 | EXECUTE_IF_SET_IN_BITMAP (interesting, 0, i, bi) | |
2e96b5f1 | 371 | { |
d86d81a4 RB |
372 | tree name = ssa_name (i); |
373 | gimple *def_stmt = SSA_NAME_DEF_STMT (name); | |
374 | if (gimple_bb (def_stmt) != bb) | |
375 | bitmap_set_bit (new_interesting, i); | |
376 | else if (gphi *phi = dyn_cast<gphi *> (def_stmt)) | |
377 | { | |
378 | tree res = gimple_phi_result (phi); | |
379 | if (!SSA_NAME_OCCURS_IN_ABNORMAL_PHI (res)) | |
380 | interesting_phis.safe_push (phi); | |
381 | } | |
2e96b5f1 | 382 | } |
d86d81a4 RB |
383 | if (!bitmap_empty_p (new_interesting) |
384 | || !interesting_phis.is_empty ()) | |
98b212c1 | 385 | { |
d86d81a4 | 386 | auto_vec<tree, 4> unwind (interesting_phis.length ()); |
98b212c1 AH |
387 | edge_iterator iter; |
388 | edge e; | |
389 | FOR_EACH_EDGE (e, iter, bb->preds) | |
d86d81a4 RB |
390 | { |
391 | if (e->flags & EDGE_ABNORMAL | |
392 | // This is like path_crosses_loops in profitable_path_p but | |
393 | // more restrictive to avoid peeling off loop iterations (see | |
394 | // tree-ssa/pr14341.c for an example). | |
395 | // ??? Note this restriction only applied when visiting an | |
396 | // interesting PHI with the former resolve_phi. | |
397 | || (!interesting_phis.is_empty () | |
398 | && m_path[0]->loop_father != e->src->loop_father)) | |
399 | continue; | |
400 | for (gphi *phi : interesting_phis) | |
401 | { | |
402 | tree def = PHI_ARG_DEF_FROM_EDGE (phi, e); | |
403 | if (TREE_CODE (def) == SSA_NAME) | |
404 | if (bitmap_set_bit (new_interesting, | |
405 | SSA_NAME_VERSION (def))) | |
406 | { | |
407 | bitmap_set_bit (m_imports, SSA_NAME_VERSION (def)); | |
408 | unwind.quick_push (def); | |
409 | } | |
410 | } | |
411 | find_paths_to_names (e->src, new_interesting); | |
412 | // Restore new_interesting. We leave m_imports alone since | |
413 | // we do not prune defs in BB from it and separately keeping | |
414 | // track of which bits to unwind isn't worth the trouble. | |
415 | for (tree def : unwind) | |
416 | bitmap_clear_bit (new_interesting, SSA_NAME_VERSION (def)); | |
417 | unwind.truncate (0); | |
418 | } | |
98b212c1 | 419 | } |
2e96b5f1 AH |
420 | } |
421 | ||
98b212c1 | 422 | // Reset things to their original state. |
2e96b5f1 AH |
423 | m_path.pop (); |
424 | m_visited_bbs.remove (bb); | |
2e96b5f1 AH |
425 | } |
426 | ||
427 | // Search backwards from BB looking for paths where the final | |
428 | // conditional out of BB can be determined. NAME is the LHS of the | |
429 | // final conditional. Register such paths for jump threading. | |
430 | ||
431 | void | |
432 | back_threader::find_paths (basic_block bb, tree name) | |
433 | { | |
434 | gimple *stmt = last_stmt (bb); | |
435 | if (!stmt | |
436 | || (gimple_code (stmt) != GIMPLE_COND | |
437 | && gimple_code (stmt) != GIMPLE_SWITCH)) | |
438 | return; | |
439 | ||
440 | if (EDGE_COUNT (bb->succs) > 1 | |
441 | || single_succ_to_potentially_threadable_block (bb)) | |
442 | { | |
443 | m_last_stmt = stmt; | |
444 | m_visited_bbs.empty (); | |
445 | m_path.truncate (0); | |
446 | m_name = name; | |
bfa04d0e | 447 | m_solver->compute_imports (m_imports, bb); |
2e96b5f1 AH |
448 | |
449 | auto_bitmap interesting; | |
2e96b5f1 AH |
450 | bitmap_copy (interesting, m_imports); |
451 | find_paths_to_names (bb, interesting); | |
452 | } | |
453 | } | |
454 | ||
34cd97ff AH |
455 | // Simple helper to get the last statement from BB, which is assumed |
456 | // to be a control statement. Return NULL if the last statement is | |
457 | // not a control statement. | |
21f0717a | 458 | |
3d466672 JL |
459 | static gimple * |
460 | get_gimple_control_stmt (basic_block bb) | |
461 | { | |
21f0717a | 462 | gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb); |
3d466672 JL |
463 | |
464 | if (gsi_end_p (gsi)) | |
465 | return NULL; | |
466 | ||
467 | gimple *stmt = gsi_stmt (gsi); | |
468 | enum gimple_code code = gimple_code (stmt); | |
21f0717a JL |
469 | if (code == GIMPLE_COND || code == GIMPLE_SWITCH || code == GIMPLE_GOTO) |
470 | return stmt; | |
471 | return NULL; | |
3d466672 JL |
472 | } |
473 | ||
34cd97ff AH |
474 | // Search backwards from BB looking for paths where the final |
475 | // conditional maybe threaded to a successor block. Record such paths | |
476 | // for jump threading. | |
0732f75f | 477 | |
34cd97ff AH |
478 | void |
479 | back_threader::maybe_thread_block (basic_block bb) | |
0732f75f | 480 | { |
34cd97ff AH |
481 | gimple *stmt = get_gimple_control_stmt (bb); |
482 | if (!stmt) | |
483 | return; | |
0732f75f | 484 | |
34cd97ff AH |
485 | enum gimple_code code = gimple_code (stmt); |
486 | tree name; | |
487 | if (code == GIMPLE_SWITCH) | |
488 | name = gimple_switch_index (as_a <gswitch *> (stmt)); | |
489 | else if (code == GIMPLE_COND) | |
490 | name = gimple_cond_lhs (stmt); | |
491 | else if (code == GIMPLE_GOTO) | |
492 | name = gimple_goto_dest (stmt); | |
493 | else | |
494 | name = NULL; | |
495 | ||
496 | find_paths (bb, name); | |
497 | } | |
498 | ||
34cd97ff AH |
499 | DEBUG_FUNCTION void |
500 | debug (const vec <basic_block> &path) | |
501 | { | |
502 | dump_path (stderr, path); | |
a2ab1a5a | 503 | fputc ('\n', stderr); |
0732f75f JL |
504 | } |
505 | ||
779275c0 AH |
506 | void |
507 | back_threader::dump (FILE *out) | |
508 | { | |
4e0f56d7 | 509 | m_solver->dump (out); |
779275c0 AH |
510 | fprintf (out, "\nCandidates for pre-computation:\n"); |
511 | fprintf (out, "===================================\n"); | |
512 | ||
513 | bitmap_iterator bi; | |
514 | unsigned i; | |
515 | ||
516 | EXECUTE_IF_SET_IN_BITMAP (m_imports, 0, i, bi) | |
517 | { | |
518 | tree name = ssa_name (i); | |
519 | print_generic_expr (out, name, TDF_NONE); | |
520 | fprintf (out, "\n"); | |
521 | } | |
522 | } | |
523 | ||
524 | void | |
525 | back_threader::debug () | |
526 | { | |
527 | dump (stderr); | |
528 | } | |
529 | ||
69e55442 AH |
530 | /* Examine jump threading path PATH and return TRUE if it is profitable to |
531 | thread it, otherwise return FALSE. | |
bb5e62d6 JL |
532 | |
533 | NAME is the SSA_NAME of the variable we found to have a constant | |
69e55442 | 534 | value on PATH. If unknown, SSA_NAME is NULL. |
bb5e62d6 | 535 | |
69e55442 AH |
536 | If the taken edge out of the path is known ahead of time it is passed in |
537 | TAKEN_EDGE, otherwise it is NULL. | |
bb5e62d6 | 538 | |
69e55442 | 539 | CREATES_IRREDUCIBLE_LOOP, if non-null is set to TRUE if threading this path |
01b50387 AH |
540 | would create an irreducible loop. |
541 | ||
542 | ?? It seems we should be able to loosen some of the restrictions in | |
543 | this function after loop optimizations have run. */ | |
69e55442 AH |
544 | |
545 | bool | |
546 | back_threader_profitability::profitable_path_p (const vec<basic_block> &m_path, | |
547 | tree name, | |
548 | edge taken_edge, | |
549 | bool *creates_irreducible_loop) | |
bb5e62d6 | 550 | { |
69e55442 | 551 | gcc_checking_assert (!m_path.is_empty ()); |
ad071b2b | 552 | |
69e55442 AH |
553 | /* We can an empty path here (excluding the DEF block) when the |
554 | statement that makes a conditional generate a compile-time | |
555 | constant result is in the same block as the conditional. | |
ad071b2b JL |
556 | |
557 | That's not really a jump threading opportunity, but instead is | |
558 | simple cprop & simplification. We could handle it here if we | |
559 | wanted by wiring up all the incoming edges. If we run this | |
560 | early in IPA, that might be worth doing. For now we just | |
561 | reject that case. */ | |
69e55442 AH |
562 | if (m_path.length () <= 1) |
563 | return false; | |
ad071b2b | 564 | |
82cd78f2 AH |
565 | if (m_path.length () > (unsigned) param_max_fsm_thread_length) |
566 | { | |
567 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
568 | fprintf (dump_file, " FAIL: Jump-thread path not considered: " | |
569 | "the number of basic blocks on the path " | |
570 | "exceeds PARAM_MAX_FSM_THREAD_LENGTH.\n"); | |
571 | return false; | |
572 | } | |
573 | ||
bb5e62d6 JL |
574 | int n_insns = 0; |
575 | gimple_stmt_iterator gsi; | |
cde30fe0 | 576 | loop_p loop = m_path[0]->loop_father; |
bb5e62d6 JL |
577 | bool threaded_through_latch = false; |
578 | bool multiway_branch_in_path = false; | |
579 | bool threaded_multiway_branch = false; | |
0f0c2cc3 JH |
580 | bool contains_hot_bb = false; |
581 | ||
582 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
583 | fprintf (dump_file, "Checking profitability of path (backwards): "); | |
bb5e62d6 JL |
584 | |
585 | /* Count the number of instructions on the path: as these instructions | |
586 | will have to be duplicated, we will not record the path if there | |
587 | are too many instructions on the path. Also check that all the | |
588 | blocks in the path belong to a single loop. */ | |
cde30fe0 | 589 | for (unsigned j = 0; j < m_path.length (); j++) |
bb5e62d6 | 590 | { |
cde30fe0 | 591 | basic_block bb = m_path[j]; |
bb5e62d6 | 592 | |
0f0c2cc3 JH |
593 | if (dump_file && (dump_flags & TDF_DETAILS)) |
594 | fprintf (dump_file, " bb:%i", bb->index); | |
a7753db4 AH |
595 | /* Remember, blocks in the path are stored in opposite order in |
596 | the PATH array. The last entry in the array represents the | |
597 | block with an outgoing edge that we will redirect to the jump | |
598 | threading path. Thus we don't care how many statements are | |
599 | in that block because it will not be copied or whether or not | |
600 | it ends in a multiway branch. */ | |
cde30fe0 | 601 | if (j < m_path.length () - 1) |
bb5e62d6 | 602 | { |
0f0c2cc3 | 603 | int orig_n_insns = n_insns; |
bb5e62d6 JL |
604 | /* PHIs in the path will create degenerate PHIS in the |
605 | copied path which will then get propagated away, so | |
606 | looking at just the duplicate path the PHIs would | |
607 | seem unimportant. | |
608 | ||
609 | But those PHIs, because they're assignments to objects | |
610 | typically with lives that exist outside the thread path, | |
611 | will tend to generate PHIs (or at least new PHI arguments) | |
612 | at points where we leave the thread path and rejoin | |
613 | the original blocks. So we do want to account for them. | |
614 | ||
615 | We ignore virtual PHIs. We also ignore cases where BB | |
616 | has a single incoming edge. That's the most common | |
617 | degenerate PHI we'll see here. Finally we ignore PHIs | |
618 | that are associated with the value we're tracking as | |
619 | that object likely dies. */ | |
620 | if (EDGE_COUNT (bb->succs) > 1 && EDGE_COUNT (bb->preds) > 1) | |
621 | { | |
622 | for (gphi_iterator gsip = gsi_start_phis (bb); | |
623 | !gsi_end_p (gsip); | |
624 | gsi_next (&gsip)) | |
625 | { | |
626 | gphi *phi = gsip.phi (); | |
627 | tree dst = gimple_phi_result (phi); | |
628 | ||
629 | /* Note that if both NAME and DST are anonymous | |
630 | SSA_NAMEs, then we do not have enough information | |
631 | to consider them associated. */ | |
fda5c810 | 632 | if (dst != name |
69e55442 AH |
633 | && name |
634 | && TREE_CODE (name) == SSA_NAME | |
fda5c810 RB |
635 | && (SSA_NAME_VAR (dst) != SSA_NAME_VAR (name) |
636 | || !SSA_NAME_VAR (dst)) | |
bb5e62d6 JL |
637 | && !virtual_operand_p (dst)) |
638 | ++n_insns; | |
639 | } | |
640 | } | |
641 | ||
cde30fe0 | 642 | if (!contains_hot_bb && m_speed_p) |
0f0c2cc3 | 643 | contains_hot_bb |= optimize_bb_for_speed_p (bb); |
bb5e62d6 JL |
644 | for (gsi = gsi_after_labels (bb); |
645 | !gsi_end_p (gsi); | |
646 | gsi_next_nondebug (&gsi)) | |
647 | { | |
70a62009 IL |
648 | /* Do not allow OpenACC loop markers and __builtin_constant_p on |
649 | threading paths. The latter is disallowed, because an | |
650 | expression might be constant on two threading paths, and | |
651 | become non-constant (i.e.: phi) when they merge. */ | |
bb5e62d6 | 652 | gimple *stmt = gsi_stmt (gsi); |
70a62009 IL |
653 | if (gimple_call_internal_p (stmt, IFN_UNIQUE) |
654 | || gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P)) | |
69e55442 | 655 | return false; |
bb5e62d6 JL |
656 | /* Do not count empty statements and labels. */ |
657 | if (gimple_code (stmt) != GIMPLE_NOP | |
bb5e62d6 | 658 | && !is_gimple_debug (stmt)) |
0f0c2cc3 | 659 | n_insns += estimate_num_insns (stmt, &eni_size_weights); |
bb5e62d6 | 660 | } |
0f0c2cc3 JH |
661 | if (dump_file && (dump_flags & TDF_DETAILS)) |
662 | fprintf (dump_file, " (%i insns)", n_insns-orig_n_insns); | |
bb5e62d6 JL |
663 | |
664 | /* We do not look at the block with the threaded branch | |
665 | in this loop. So if any block with a last statement that | |
666 | is a GIMPLE_SWITCH or GIMPLE_GOTO is seen, then we have a | |
667 | multiway branch on our path. | |
668 | ||
669 | The block in PATH[0] is special, it's the block were we're | |
670 | going to be able to eliminate its branch. */ | |
671 | gimple *last = last_stmt (bb); | |
672 | if (last && (gimple_code (last) == GIMPLE_SWITCH | |
673 | || gimple_code (last) == GIMPLE_GOTO)) | |
674 | { | |
675 | if (j == 0) | |
676 | threaded_multiway_branch = true; | |
677 | else | |
678 | multiway_branch_in_path = true; | |
679 | } | |
680 | } | |
681 | ||
682 | /* Note if we thread through the latch, we will want to include | |
683 | the last entry in the array when determining if we thread | |
684 | through the loop latch. */ | |
685 | if (loop->latch == bb) | |
01b50387 AH |
686 | { |
687 | threaded_through_latch = true; | |
688 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
689 | fprintf (dump_file, " (latch)"); | |
690 | } | |
bb5e62d6 JL |
691 | } |
692 | ||
cde30fe0 | 693 | gimple *stmt = get_gimple_control_stmt (m_path[0]); |
27bddc4a JH |
694 | gcc_assert (stmt); |
695 | ||
bb5e62d6 JL |
696 | /* We are going to remove the control statement at the end of the |
697 | last block in the threading path. So don't count it against our | |
698 | statement count. */ | |
bb5e62d6 | 699 | |
0f0c2cc3 JH |
700 | int stmt_insns = estimate_num_insns (stmt, &eni_size_weights); |
701 | n_insns-= stmt_insns; | |
702 | ||
703 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
704 | fprintf (dump_file, "\n Control statement insns: %i\n" | |
705 | " Overall: %i insns\n", | |
706 | stmt_insns, n_insns); | |
27bddc4a | 707 | |
69e55442 | 708 | if (creates_irreducible_loop) |
bb5e62d6 | 709 | { |
69e55442 AH |
710 | /* If this path threaded through the loop latch back into the |
711 | same loop and the destination does not dominate the loop | |
712 | latch, then this thread would create an irreducible loop. */ | |
713 | *creates_irreducible_loop = false; | |
714 | if (taken_edge | |
715 | && threaded_through_latch | |
716 | && loop == taken_edge->dest->loop_father | |
717 | && (determine_bb_domination_status (loop, taken_edge->dest) | |
718 | == DOMST_NONDOMINATING)) | |
719 | *creates_irreducible_loop = true; | |
bb5e62d6 JL |
720 | } |
721 | ||
0f0c2cc3 JH |
722 | /* Threading is profitable if the path duplicated is hot but also |
723 | in a case we separate cold path from hot path and permit optimization | |
724 | of the hot path later. Be on the agressive side here. In some testcases, | |
725 | as in PR 78407 this leads to noticeable improvements. */ | |
69e55442 AH |
726 | if (m_speed_p |
727 | && ((taken_edge && optimize_edge_for_speed_p (taken_edge)) | |
728 | || contains_hot_bb)) | |
27bddc4a | 729 | { |
028d4092 | 730 | if (n_insns >= param_max_fsm_thread_path_insns) |
27bddc4a JH |
731 | { |
732 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
c7a669af | 733 | fprintf (dump_file, " FAIL: Jump-thread path not considered: " |
27bddc4a JH |
734 | "the number of instructions on the path " |
735 | "exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n"); | |
69e55442 | 736 | return false; |
27bddc4a | 737 | } |
b9da6864 RB |
738 | if (taken_edge && probably_never_executed_edge_p (cfun, taken_edge)) |
739 | { | |
740 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
741 | fprintf (dump_file, " FAIL: Jump-thread path not considered: " | |
742 | "path leads to probably never executed edge.\n"); | |
743 | return false; | |
744 | } | |
49ba4fde RB |
745 | edge entry = find_edge (m_path[m_path.length () - 1], |
746 | m_path[m_path.length () - 2]); | |
747 | if (probably_never_executed_edge_p (cfun, entry)) | |
748 | { | |
749 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
750 | fprintf (dump_file, " FAIL: Jump-thread path not considered: " | |
751 | "path entry is probably never executed.\n"); | |
752 | return false; | |
753 | } | |
27bddc4a | 754 | } |
353fd1ec | 755 | else if (n_insns > 1) |
bb5e62d6 JL |
756 | { |
757 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
c7a669af | 758 | fprintf (dump_file, " FAIL: Jump-thread path not considered: " |
27bddc4a JH |
759 | "duplication of %i insns is needed and optimizing for size.\n", |
760 | n_insns); | |
69e55442 | 761 | return false; |
bb5e62d6 JL |
762 | } |
763 | ||
764 | /* We avoid creating irreducible inner loops unless we thread through | |
765 | a multiway branch, in which case we have deemed it worth losing | |
766 | other loop optimizations later. | |
767 | ||
768 | We also consider it worth creating an irreducible inner loop if | |
769 | the number of copied statement is low relative to the length of | |
770 | the path -- in that case there's little the traditional loop | |
771 | optimizer would have done anyway, so an irreducible loop is not | |
772 | so bad. */ | |
69e55442 AH |
773 | if (!threaded_multiway_branch |
774 | && creates_irreducible_loop | |
775 | && *creates_irreducible_loop | |
028d4092 | 776 | && (n_insns * (unsigned) param_fsm_scale_path_stmts |
cde30fe0 | 777 | > (m_path.length () * |
028d4092 | 778 | (unsigned) param_fsm_scale_path_blocks))) |
bb5e62d6 JL |
779 | |
780 | { | |
781 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
782 | fprintf (dump_file, | |
c7a669af | 783 | " FAIL: Would create irreducible loop without threading " |
bb5e62d6 | 784 | "multiway branch.\n"); |
69e55442 | 785 | return false; |
bb5e62d6 JL |
786 | } |
787 | ||
c7a669af AH |
788 | /* The generic copier used by the backthreader does not re-use an |
789 | existing threading path to reduce code duplication. So for that | |
790 | case, drastically reduce the number of statements we are allowed | |
791 | to copy. */ | |
bb5e62d6 | 792 | if (!(threaded_through_latch && threaded_multiway_branch) |
028d4092 ML |
793 | && (n_insns * param_fsm_scale_path_stmts |
794 | >= param_max_jump_thread_duplication_stmts)) | |
bb5e62d6 JL |
795 | { |
796 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
797 | fprintf (dump_file, | |
c7a669af | 798 | " FAIL: Did not thread around loop and would copy too " |
bb5e62d6 | 799 | "many statements.\n"); |
69e55442 | 800 | return false; |
bb5e62d6 JL |
801 | } |
802 | ||
803 | /* When there is a multi-way branch on the path, then threading can | |
804 | explode the CFG due to duplicating the edges for that multi-way | |
805 | branch. So like above, only allow a multi-way branch on the path | |
806 | if we actually thread a multi-way branch. */ | |
807 | if (!threaded_multiway_branch && multiway_branch_in_path) | |
808 | { | |
809 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
810 | fprintf (dump_file, | |
c7a669af | 811 | " FAIL: Thread through multiway branch without threading " |
bb5e62d6 | 812 | "a multiway branch.\n"); |
69e55442 | 813 | return false; |
bb5e62d6 | 814 | } |
01b50387 AH |
815 | |
816 | /* Threading through an empty latch would cause code to be added to | |
817 | the latch. This could alter the loop form sufficiently to cause | |
818 | loop optimizations to fail. Disable these threads until after | |
819 | loop optimizations have run. */ | |
820 | if ((threaded_through_latch | |
821 | || (taken_edge && taken_edge->dest == loop->latch)) | |
822 | && !(cfun->curr_properties & PROP_loop_opts_done) | |
823 | && empty_block_p (loop->latch)) | |
824 | { | |
825 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
826 | fprintf (dump_file, | |
c7a669af | 827 | " FAIL: Thread through latch before loop opts would create non-empty latch\n"); |
01b50387 AH |
828 | return false; |
829 | ||
830 | } | |
69e55442 AH |
831 | return true; |
832 | } | |
833 | ||
cde30fe0 AH |
834 | /* The current path PATH is a vector of blocks forming a jump threading |
835 | path in reverse order. TAKEN_EDGE is the edge taken from path[0]. | |
081fdda6 | 836 | |
cde30fe0 | 837 | Convert the current path into the form used by register_jump_thread and |
69e55442 | 838 | register it. |
081fdda6 | 839 | |
69e55442 AH |
840 | Return TRUE if successful or FALSE otherwise. */ |
841 | ||
842 | bool | |
843 | back_threader_registry::register_path (const vec<basic_block> &m_path, | |
844 | edge taken_edge) | |
081fdda6 | 845 | { |
7f6c2258 | 846 | vec<jump_thread_edge *> *jump_thread_path = allocate_thread_path (); |
081fdda6 | 847 | |
c7a669af AH |
848 | // The generic copier ignores the edge type. We can build the |
849 | // thread edges with any type. | |
cde30fe0 | 850 | for (unsigned int j = 0; j + 1 < m_path.length (); j++) |
081fdda6 | 851 | { |
cde30fe0 AH |
852 | basic_block bb1 = m_path[m_path.length () - j - 1]; |
853 | basic_block bb2 = m_path[m_path.length () - j - 2]; | |
8af01c66 | 854 | |
081fdda6 JL |
855 | edge e = find_edge (bb1, bb2); |
856 | gcc_assert (e); | |
7f6c2258 | 857 | push_edge (jump_thread_path, e, EDGE_COPY_SRC_BLOCK); |
081fdda6 JL |
858 | } |
859 | ||
7f6c2258 AH |
860 | push_edge (jump_thread_path, taken_edge, EDGE_NO_COPY_SRC_BLOCK); |
861 | register_jump_thread (jump_thread_path); | |
69e55442 | 862 | return true; |
081fdda6 JL |
863 | } |
864 | ||
4e0f56d7 | 865 | // Thread all suitable paths in the current function. |
5d4d64fa | 866 | // |
4e0f56d7 | 867 | // Return TODO_flags. |
8b2ef235 | 868 | |
4e0f56d7 AH |
869 | unsigned int |
870 | back_threader::thread_blocks () | |
8b2ef235 | 871 | { |
8b2ef235 | 872 | basic_block bb; |
4e0f56d7 AH |
873 | FOR_EACH_BB_FN (bb, m_fun) |
874 | if (EDGE_COUNT (bb->succs) > 1) | |
875 | maybe_thread_block (bb); | |
bb7ebad1 | 876 | |
4e0f56d7 | 877 | bool changed = m_registry.thread_through_all_blocks (true); |
5d4d64fa | 878 | |
4e0f56d7 AH |
879 | if (m_flags & BT_SPEED) |
880 | return changed ? TODO_cleanup_cfg : 0; | |
5d4d64fa | 881 | |
4e0f56d7 | 882 | return false; |
8b2ef235 | 883 | } |
b720e919 JH |
884 | |
885 | namespace { | |
886 | ||
887 | const pass_data pass_data_early_thread_jumps = | |
888 | { | |
889 | GIMPLE_PASS, | |
890 | "ethread", | |
891 | OPTGROUP_NONE, | |
892 | TV_TREE_SSA_THREAD_JUMPS, | |
893 | ( PROP_cfg | PROP_ssa ), | |
894 | 0, | |
895 | 0, | |
896 | 0, | |
897 | ( TODO_cleanup_cfg | TODO_update_ssa ), | |
898 | }; | |
899 | ||
5d4d64fa AH |
900 | const pass_data pass_data_thread_jumps = |
901 | { | |
902 | GIMPLE_PASS, | |
903 | "thread", | |
904 | OPTGROUP_NONE, | |
905 | TV_TREE_SSA_THREAD_JUMPS, | |
906 | ( PROP_cfg | PROP_ssa ), | |
907 | 0, | |
908 | 0, | |
909 | 0, | |
910 | TODO_update_ssa, | |
911 | }; | |
912 | ||
913 | const pass_data pass_data_thread_jumps_full = | |
914 | { | |
915 | GIMPLE_PASS, | |
4b3a325f | 916 | "threadfull", |
5d4d64fa AH |
917 | OPTGROUP_NONE, |
918 | TV_TREE_SSA_THREAD_JUMPS, | |
919 | ( PROP_cfg | PROP_ssa ), | |
920 | 0, | |
921 | 0, | |
922 | 0, | |
923 | TODO_update_ssa, | |
924 | }; | |
925 | ||
926 | // Early jump threading pass optimizing for size. | |
b720e919 JH |
927 | class pass_early_thread_jumps : public gimple_opt_pass |
928 | { | |
929 | public: | |
930 | pass_early_thread_jumps (gcc::context *ctxt) | |
931 | : gimple_opt_pass (pass_data_early_thread_jumps, ctxt) | |
932 | {} | |
933 | ||
5d4d64fa AH |
934 | opt_pass * clone () override |
935 | { | |
936 | return new pass_early_thread_jumps (m_ctxt); | |
937 | } | |
bc5baac5 AH |
938 | void set_pass_param (unsigned int, bool param) override |
939 | { | |
940 | m_first = param; | |
941 | } | |
5d4d64fa AH |
942 | bool gate (function *) override |
943 | { | |
944 | return flag_thread_jumps; | |
945 | } | |
946 | unsigned int execute (function *fun) override | |
947 | { | |
bc5baac5 | 948 | back_threader threader (fun, BT_NONE, m_first); |
4e0f56d7 | 949 | return threader.thread_blocks (); |
5d4d64fa | 950 | } |
bc5baac5 AH |
951 | private: |
952 | bool m_first; | |
b720e919 JH |
953 | }; |
954 | ||
5d4d64fa AH |
955 | // Jump threading pass without resolving of unknown SSAs. |
956 | class pass_thread_jumps : public gimple_opt_pass | |
b720e919 | 957 | { |
5d4d64fa AH |
958 | public: |
959 | pass_thread_jumps (gcc::context *ctxt) | |
960 | : gimple_opt_pass (pass_data_thread_jumps, ctxt) | |
961 | {} | |
962 | opt_pass * clone (void) override | |
963 | { | |
964 | return new pass_thread_jumps (m_ctxt); | |
965 | } | |
bc5baac5 AH |
966 | void set_pass_param (unsigned int, bool param) override |
967 | { | |
968 | m_first = param; | |
969 | } | |
5d4d64fa AH |
970 | bool gate (function *) override |
971 | { | |
972 | return flag_thread_jumps && flag_expensive_optimizations; | |
973 | } | |
974 | unsigned int execute (function *fun) override | |
975 | { | |
bc5baac5 | 976 | back_threader threader (fun, BT_SPEED, m_first); |
4e0f56d7 | 977 | return threader.thread_blocks (); |
5d4d64fa | 978 | } |
bc5baac5 AH |
979 | private: |
980 | bool m_first; | |
5d4d64fa | 981 | }; |
b720e919 | 982 | |
5d4d64fa AH |
983 | // Jump threading pass that fully resolves unknown SSAs. |
984 | class pass_thread_jumps_full : public gimple_opt_pass | |
b720e919 | 985 | { |
5d4d64fa AH |
986 | public: |
987 | pass_thread_jumps_full (gcc::context *ctxt) | |
988 | : gimple_opt_pass (pass_data_thread_jumps_full, ctxt) | |
989 | {} | |
990 | opt_pass * clone (void) override | |
991 | { | |
dece6ae7 | 992 | return new pass_thread_jumps_full (m_ctxt); |
5d4d64fa | 993 | } |
bc5baac5 AH |
994 | void set_pass_param (unsigned int, bool param) override |
995 | { | |
996 | m_first = param; | |
997 | } | |
5d4d64fa AH |
998 | bool gate (function *) override |
999 | { | |
1000 | return flag_thread_jumps && flag_expensive_optimizations; | |
1001 | } | |
1002 | unsigned int execute (function *fun) override | |
1003 | { | |
bc5baac5 | 1004 | back_threader threader (fun, BT_SPEED | BT_RESOLVE, m_first); |
4e0f56d7 | 1005 | return threader.thread_blocks (); |
5d4d64fa | 1006 | } |
bc5baac5 AH |
1007 | private: |
1008 | bool m_first; | |
5d4d64fa | 1009 | }; |
108fdd6d | 1010 | |
5d4d64fa | 1011 | } // namespace { |
108fdd6d | 1012 | |
5d4d64fa AH |
1013 | gimple_opt_pass * |
1014 | make_pass_thread_jumps (gcc::context *ctxt) | |
1015 | { | |
1016 | return new pass_thread_jumps (ctxt); | |
b720e919 JH |
1017 | } |
1018 | ||
5d4d64fa AH |
1019 | gimple_opt_pass * |
1020 | make_pass_thread_jumps_full (gcc::context *ctxt) | |
1021 | { | |
1022 | return new pass_thread_jumps_full (ctxt); | |
b720e919 JH |
1023 | } |
1024 | ||
1025 | gimple_opt_pass * | |
1026 | make_pass_early_thread_jumps (gcc::context *ctxt) | |
1027 | { | |
1028 | return new pass_early_thread_jumps (ctxt); | |
1029 | } |