1 /*
2 * Copyright 2023 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "nir/nir_builder.h"
7 #include "nir.h"
8 #include "nir_control_flow.h"
9 #include "nir_loop_analyze.h"
10
11 static bool
is_block_empty(nir_block * block)12 is_block_empty(nir_block *block)
13 {
14 return nir_cf_node_is_last(&block->cf_node) &&
15 exec_list_is_empty(&block->instr_list);
16 }
17
18 static bool
is_block_singular(nir_block * block)19 is_block_singular(nir_block *block)
20 {
21 return nir_cf_node_is_last(&block->cf_node) &&
22 (exec_list_is_empty(&block->instr_list) ||
23 (exec_list_is_singular(&block->instr_list) && nir_block_ends_in_jump(block)));
24 }
25
26 static bool
nir_block_ends_in_continue(nir_block * block)27 nir_block_ends_in_continue(nir_block *block)
28 {
29 if (exec_list_is_empty(&block->instr_list))
30 return false;
31
32 nir_instr *instr = nir_block_last_instr(block);
33 return instr->type == nir_instr_type_jump &&
34 nir_instr_as_jump(instr)->type == nir_jump_continue;
35 }
36
37 /**
38 * This optimization tries to merge two equal jump instructions (break or
39 * continue) into a single one.
40 *
41 * This optimization turns
42 *
43 * loop {
44 * ...
45 * if (cond) {
46 * do_work_1();
47 * break;
48 * } else {
49 * do_work_2();
50 * break;
51 * }
52 * }
53 *
54 * into:
55 *
56 * loop {
57 * ...
58 * if (cond) {
59 * do_work_1();
60 * } else {
61 * do_work_2();
62 * }
63 * break;
64 * }
65 *
66 * It does the same with continue statements, respectively.
67 *
68 */
69 static bool
opt_loop_merge_break_continue(nir_if * nif)70 opt_loop_merge_break_continue(nir_if *nif)
71 {
72 nir_block *after_if = nir_cf_node_cf_tree_next(&nif->cf_node);
73
74 /* The block after the IF must have no predecessors and be empty. */
75 if (after_if->predecessors->entries > 0 || !is_block_empty(after_if))
76 return false;
77
78 nir_block *last_then = nir_if_last_then_block(nif);
79 nir_block *last_else = nir_if_last_else_block(nif);
80 const bool then_break = nir_block_ends_in_break(last_then);
81 const bool else_break = nir_block_ends_in_break(last_else);
82 const bool then_cont = nir_block_ends_in_continue(last_then);
83 const bool else_cont = nir_block_ends_in_continue(last_else);
84
85 /* If both branch legs end with the same jump instruction,
86 * merge the statement after the branch
87 */
88 if ((then_break && else_break) || (then_cont && else_cont)) {
89 nir_lower_phis_to_regs_block(last_then->successors[0]);
90 nir_instr_remove_v(nir_block_last_instr(last_then));
91 nir_instr *jump = nir_block_last_instr(last_else);
92 nir_instr_remove_v(jump);
93 nir_instr_insert(nir_after_block(after_if), jump);
94 return true;
95 }
96
97 return false;
98 }
99
100 /**
101 * This optimization simplifies potential loop terminators which then allows
102 * other passes such as opt_if_simplification() and loop unrolling to progress
103 * further:
104 *
105 * if (cond) {
106 * ... then block instructions ...
107 * } else {
108 * ...
109 * break;
110 * }
111 *
112 * into:
113 *
114 * if (cond) {
115 * } else {
116 * ...
117 * break;
118 * }
119 * ... then block instructions ...
120 */
121 static bool
opt_loop_terminator(nir_if * nif)122 opt_loop_terminator(nir_if *nif)
123 {
124 nir_block *break_blk = NULL;
125 nir_block *continue_from_blk = NULL;
126 nir_block *first_continue_from_blk = NULL;
127
128 nir_block *last_then = nir_if_last_then_block(nif);
129 nir_block *last_else = nir_if_last_else_block(nif);
130
131 if (nir_block_ends_in_break(last_then)) {
132 break_blk = last_then;
133 continue_from_blk = last_else;
134 first_continue_from_blk = nir_if_first_else_block(nif);
135 } else if (nir_block_ends_in_break(last_else)) {
136 break_blk = last_else;
137 continue_from_blk = last_then;
138 first_continue_from_blk = nir_if_first_then_block(nif);
139 }
140
141 /* Continue if the if-statement contained no jumps at all */
142 if (!break_blk)
143 return false;
144
145 /* If the continue from block is empty then return as there is nothing to
146 * move.
147 */
148 if (is_block_empty(first_continue_from_blk))
149 return false;
150
151 if (nir_block_ends_in_jump(continue_from_blk)) {
152 /* Let nir_opt_dead_cf() clean up any dead code. */
153 if (!is_block_empty(nir_cf_node_cf_tree_next(&nif->cf_node)))
154 return false;
155
156 /* We are about to move the predecessor. */
157 nir_lower_phis_to_regs_block(continue_from_blk->successors[0]);
158 }
159
160 /* Even though this if statement has a jump on one side, we may still have
161 * phis afterwards. Single-source phis can be produced by loop unrolling
162 * or dead control-flow passes and are perfectly legal. Run a quick phi
163 * removal on the block after the if to clean up any such phis.
164 */
165 nir_remove_single_src_phis_block(nir_cf_node_as_block(nir_cf_node_next(&nif->cf_node)));
166
167 /* Finally, move the continue from branch after the if-statement. */
168 nir_cf_list tmp;
169 nir_cf_extract(&tmp, nir_before_block(first_continue_from_blk),
170 nir_after_block(continue_from_blk));
171 nir_cf_reinsert(&tmp, nir_after_cf_node(&nif->cf_node));
172
173 return true;
174 }
175
176 /**
177 * This optimization tries to merge the jump instruction (break or continue)
178 * of a block with an equal one from a previous IF.
179 *
180 * This optimization turns:
181 *
182 * loop {
183 * ...
184 * if (cond) {
185 * do_work_1();
186 * break;
187 * } else {
188 * }
189 * do_work_2();
190 * break;
191 * }
192 *
193 * into:
194 *
195 * loop {
196 * ...
197 * if (cond) {
198 * do_work_1();
199 * } else {
200 * do_work_2();
201 * }
202 * break;
203 * }
204 *
205 * It does the same with continue statements, respectively.
206 *
207 */
208 static bool
opt_loop_last_block(nir_block * block,bool is_trivial_continue,bool is_trivial_break)209 opt_loop_last_block(nir_block *block, bool is_trivial_continue, bool is_trivial_break)
210 {
211 /* If this block has no predecessors, let nir_opt_dead_cf() do the cleanup */
212 if (block->predecessors->entries == 0)
213 return false;
214
215 bool progress = false;
216 bool has_break = nir_block_ends_in_break(block);
217 bool has_continue = nir_block_ends_in_continue(block);
218
219 /* Remove any "trivial" break and continue, i.e. those that are at the tail
220 * of a CF-list where we can just delete the instruction and
221 * control-flow will naturally take us to the same target block.
222 */
223 if ((has_break && is_trivial_break) || (has_continue && is_trivial_continue)) {
224 nir_lower_phis_to_regs_block(block->successors[0]);
225 nir_instr_remove_v(nir_block_last_instr(block));
226 return true;
227 }
228
229 if (!nir_block_ends_in_jump(block)) {
230 has_break = is_trivial_break;
231 has_continue = is_trivial_continue;
232 } else if (is_trivial_continue || is_trivial_break) {
233 /* This block ends in a jump that cannot be removed because the implicit
234 * fallthrough leads to a different target block.
235 *
236 * We already optimized this block's jump with the predecessors' when visiting
237 * this block with opt_loop_last_block(block, is_trivial_* = false, false).
238 */
239 return false;
240 }
241
242 /* Nothing to do. */
243 if (!has_continue && !has_break)
244 return false;
245
246 /* Walk backwards and check for previous IF statements whether one of the
247 * branch legs ends with an equal jump instruction as this block.
248 */
249 for (nir_cf_node *prev = nir_cf_node_prev(&block->cf_node); prev != NULL; prev = nir_cf_node_prev(prev)) {
250 /* Skip blocks and nested loops */
251 if (prev->type != nir_cf_node_if)
252 continue;
253
254 nir_if *nif = nir_cf_node_as_if(prev);
255 nir_block *then_block = nir_if_last_then_block(nif);
256 nir_block *else_block = nir_if_last_else_block(nif);
257 if (!nir_block_ends_in_jump(then_block) && !nir_block_ends_in_jump(else_block))
258 continue;
259
260 bool merge_into_then = (has_continue && nir_block_ends_in_continue(else_block)) ||
261 (has_break && nir_block_ends_in_break(else_block));
262 bool merge_into_else = (has_continue && nir_block_ends_in_continue(then_block)) ||
263 (has_break && nir_block_ends_in_break(then_block));
264
265 if (!merge_into_then && !merge_into_else)
266 continue;
267
268 /* If there are single-source phis after the IF, get rid of them first */
269 nir_remove_single_src_phis_block(nir_cf_node_cf_tree_next(prev));
270
271 /* We are about to remove one predecessor. */
272 nir_lower_phis_to_regs_block(block->successors[0]);
273
274 nir_cf_list tmp;
275 nir_cf_extract(&tmp, nir_after_cf_node(prev), nir_after_block_before_jump(block));
276
277 if (merge_into_then) {
278 nir_cf_reinsert(&tmp, nir_after_block(then_block));
279 } else {
280 nir_cf_reinsert(&tmp, nir_after_block(else_block));
281 }
282
283 /* Because we split the current block, the pointer is not valid anymore. */
284 block = nir_cf_node_cf_tree_next(prev);
285 progress = true;
286 }
287
288 /* Revisit the predecessor blocks in order to remove implicit jump instructions. */
289 if (is_block_singular(block)) {
290 nir_cf_node *prev = nir_cf_node_prev(&block->cf_node);
291 if (prev && prev->type == nir_cf_node_if) {
292 nir_if *nif = nir_cf_node_as_if(prev);
293 progress |= opt_loop_last_block(nir_if_last_then_block(nif), has_continue, has_break);
294 progress |= opt_loop_last_block(nir_if_last_else_block(nif), has_continue, has_break);
295 }
296 }
297
298 return progress;
299 }
300
301 static bool
can_constant_fold(nir_scalar scalar,nir_block * loop_header)302 can_constant_fold(nir_scalar scalar, nir_block *loop_header)
303 {
304 if (nir_scalar_is_const(scalar))
305 return true;
306
307 if (nir_scalar_is_alu(scalar)) {
308 for (unsigned i = 0; i < nir_op_infos[nir_scalar_alu_op(scalar)].num_inputs; i++) {
309 if (nir_op_infos[nir_scalar_alu_op(scalar)].input_sizes[i] > 1 ||
310 !can_constant_fold(nir_scalar_chase_alu_src(scalar, i), loop_header))
311 return false;
312 }
313 return true;
314 }
315
316 if (scalar.def->parent_instr->type == nir_instr_type_phi) {
317 /* If this is a phi from anything but the loop header, we cannot constant-fold. */
318 if (scalar.def->parent_instr->block != loop_header)
319 return false;
320
321 nir_block *preheader = nir_block_cf_tree_prev(loop_header);
322 nir_phi_instr *phi = nir_instr_as_phi(scalar.def->parent_instr);
323 nir_phi_src *src = nir_phi_get_src_from_block(phi, preheader);
324 return can_constant_fold(nir_get_scalar(src->src.ssa, 0), loop_header);
325 }
326
327 return false;
328 }
329
330 /**
331 * This optimization tries to peel the first loop break.
332 *
333 * This optimization turns:
334 *
335 * loop {
336 * do_work_1();
337 * if (cond) {
338 * break;
339 * } else {
340 * }
341 * do_work_2();
342 * }
343 *
344 * into:
345 *
346 * do_work_1();
347 * if (cond) {
348 * } else {
349 * loop {
350 * do_work_2();
351 * do_work_1();
352 * if (cond) {
353 * break;
354 * } else {
355 * }
356 * }
357 * }
358 *
359 * nir_opt_dead_cf() can later remove the outer IF statement, again.
360 *
361 */
362 static bool
opt_loop_peel_initial_break(nir_loop * loop)363 opt_loop_peel_initial_break(nir_loop *loop)
364 {
365 nir_block *header_block = nir_loop_first_block(loop);
366 nir_block *prev_block = nir_cf_node_cf_tree_prev(&loop->cf_node);
367 nir_block *exit_block = nir_cf_node_cf_tree_next(&loop->cf_node);
368
369 /* The loop must have exactly one continue block. */
370 if (header_block->predecessors->entries != 2)
371 return false;
372
373 nir_cf_node *if_node = nir_cf_node_next(&header_block->cf_node);
374 if (!if_node || if_node->type != nir_cf_node_if)
375 return false;
376
377 nir_if *nif = nir_cf_node_as_if(if_node);
378 nir_block *last_then = nir_if_last_then_block(nif);
379 if (!nir_block_ends_in_break(last_then) ||
380 !is_block_empty(nir_if_first_else_block(nif)) ||
381 !nir_is_trivial_loop_if(nif, last_then))
382 return false;
383
384 /* If do_work_2() ends in a break or other kind of jump then we can't move
385 * it to the top of the loop ahead of do_work_1().
386 */
387 if (nir_block_ends_in_jump(nir_loop_last_block(loop)))
388 return false;
389
390 /* Check that there is actual work to be done after the initial break. */
391 if (!nir_block_contains_work(nir_cf_node_cf_tree_next(if_node)))
392 return false;
393
394 /* For now, we restrict this optimization to cases where the outer IF
395 * can be constant-folded.
396 *
397 * Note: If this restriction is lifted, it might recurse infinitely.
398 * Prevent by e.g. restricting to single-exit loops.
399 */
400 if (!can_constant_fold(nir_get_scalar(nif->condition.ssa, 0), header_block))
401 return false;
402
403 /* Even though this if statement has a jump on one side, we may still have
404 * phis afterwards. Single-source phis can be produced by loop unrolling
405 * or dead control-flow passes and are perfectly legal. Run a quick phi
406 * removal on the block after the if to clean up any such phis.
407 */
408 nir_remove_single_src_phis_block(nir_cf_node_cf_tree_next(if_node));
409
410 /* We need LCSSA because we are going to wrap the loop into an IF. */
411 nir_convert_loop_to_lcssa(loop);
412
413 /* We can't lower some derefs to regs or create phis using them, so rematerialize them instead. */
414 nir_foreach_instr_safe(instr, header_block) {
415 if (instr->type == nir_instr_type_deref)
416 nir_rematerialize_deref_in_use_blocks(nir_instr_as_deref(instr));
417 }
418
419 /* Lower loop header and LCSSA-phis to regs. */
420 nir_lower_phis_to_regs_block(header_block);
421 nir_lower_ssa_defs_to_regs_block(header_block);
422 nir_lower_phis_to_regs_block(exit_block);
423
424 /* Extract the loop header including the first break. */
425 nir_cf_list tmp;
426 nir_cf_extract(&tmp, nir_before_block(header_block),
427 nir_after_cf_node(if_node));
428 header_block = nir_loop_first_block(loop);
429
430 /* Clone and re-insert at the continue block. */
431 nir_block *cont_block = nir_loop_last_block(loop);
432 struct hash_table *remap_table = _mesa_pointer_hash_table_create(NULL);
433 nir_cf_list_clone_and_reinsert(&tmp, &loop->cf_node, nir_after_block(cont_block), remap_table);
434 _mesa_hash_table_destroy(remap_table, NULL);
435
436 /* Remove the break and insert before the loop. */
437 nir_cf_reinsert(&tmp, nir_after_block(prev_block));
438 nir_instr_remove_v(nir_block_last_instr(last_then));
439
440 /* Finally, extract the entire loop and insert into the else-branch. */
441 nir_cf_extract(&tmp, nir_before_cf_node(&loop->cf_node),
442 nir_after_cf_node(&loop->cf_node));
443 nir_cf_reinsert(&tmp, nir_after_block(nir_if_first_else_block(nif)));
444
445 return true;
446 }
447
448 struct merge_term_state {
449 nir_shader *shader;
450 nir_cursor after_src_if;
451 nir_block *old_break_block;
452 nir_block *continue_block;
453 };
454
455 static bool
insert_phis_after_terminator_merge(nir_def * def,void * state)456 insert_phis_after_terminator_merge(nir_def *def, void *state)
457 {
458 struct merge_term_state *m_state = (struct merge_term_state *)state;
459
460 bool phi_created = false;
461 nir_phi_instr *phi_instr = NULL;
462
463 nir_foreach_use_including_if_safe(src, def) {
464 /* Don't reprocess the phi we just added */
465 if (!nir_src_is_if(src) && phi_instr &&
466 nir_src_parent_instr(src) == &phi_instr->instr) {
467 continue;
468 }
469
470 if (nir_src_is_if(src) ||
471 (!nir_src_is_if(src) && nir_src_parent_instr(src)->block != def->parent_instr->block)) {
472 if (!phi_created) {
473 phi_instr = nir_phi_instr_create(m_state->shader);
474 nir_def_init(&phi_instr->instr, &phi_instr->def, def->num_components,
475 def->bit_size);
476 nir_instr_insert(nir_after_block(m_state->after_src_if.block),
477 &phi_instr->instr);
478
479 nir_phi_src *phi_src =
480 nir_phi_instr_add_src(phi_instr, m_state->continue_block, def);
481 list_addtail(&phi_src->src.use_link, &def->uses);
482
483 nir_undef_instr *undef =
484 nir_undef_instr_create(m_state->shader,
485 def->num_components,
486 def->bit_size);
487 nir_instr_insert(nir_after_block(m_state->old_break_block),
488 &undef->instr);
489 phi_src = nir_phi_instr_add_src(phi_instr,
490 m_state->old_break_block,
491 &undef->def);
492 list_addtail(&phi_src->src.use_link, &undef->def.uses);
493
494 phi_created = true;
495 }
496 assert(phi_instr);
497 nir_src_rewrite(src, &phi_instr->def);
498 }
499 }
500
501 return true;
502 }
503
504 static void
merge_terminators(nir_builder * b,nir_if * dest_if,nir_if * src_if)505 merge_terminators(nir_builder *b, nir_if *dest_if, nir_if *src_if)
506 {
507 /* Move instructions from the block between the ifs into the src
508 * if-statements continue block and remove the break from the break block.
509 * This helps avoid any potential out of bounds access after the merging
510 * moves the break later.
511 */
512 bool then_break = nir_block_ends_in_break(nir_if_last_then_block(src_if));
513 nir_cursor continue_blk_c = then_break ?
514 nir_after_block(nir_if_last_else_block(src_if)) :
515 nir_after_block(nir_if_last_then_block(src_if));
516
517 nir_cf_list tmp;
518 nir_cursor after_src_if = nir_after_cf_node(&src_if->cf_node);
519 nir_cf_extract(&tmp, after_src_if, nir_before_cf_node(&dest_if->cf_node));
520 nir_cf_reinsert(&tmp, continue_blk_c);
521
522 /* Remove the break from the src if-statement */
523 nir_block *break_blk = then_break ?
524 nir_if_last_then_block(src_if) : nir_if_last_else_block(src_if);
525 nir_instr_remove(nir_block_last_instr(break_blk));
526
527 /* Add phis if needed after we moved instructions to the src if-statements
528 * continue block.
529 */
530 struct merge_term_state m_state;
531 m_state.shader = b->shader;
532 m_state.after_src_if = nir_after_cf_node(&src_if->cf_node);
533 m_state.old_break_block = break_blk;
534 m_state.continue_block = continue_blk_c.block;
535 /* Use _safe because nir_rematerialize_deref_in_use_blocks might remove dead derefs. */
536 nir_foreach_instr_reverse_safe(instr, m_state.continue_block) {
537 if (instr->type == nir_instr_type_deref)
538 nir_rematerialize_deref_in_use_blocks(nir_instr_as_deref(instr));
539 else
540 nir_foreach_def(instr, insert_phis_after_terminator_merge, &m_state);
541 }
542
543 b->cursor = nir_before_src(&dest_if->condition);
544
545 nir_def *new_c = NULL;
546 if (then_break)
547 new_c = nir_ior(b, dest_if->condition.ssa, src_if->condition.ssa);
548 else
549 new_c = nir_iand(b, dest_if->condition.ssa, src_if->condition.ssa);
550
551 nir_src_rewrite(&dest_if->condition, new_c);
552 }
553
554 /* Checks to see if the if-statement is a basic terminator containing no
555 * instructions in the branches other than a single break in one of the
556 * branches.
557 */
558 static bool
is_basic_terminator_if(nir_if * nif)559 is_basic_terminator_if(nir_if *nif)
560 {
561 nir_block *first_then = nir_if_first_then_block(nif);
562 nir_block *first_else = nir_if_first_else_block(nif);
563 nir_block *last_then = nir_if_last_then_block(nif);
564 nir_block *last_else = nir_if_last_else_block(nif);
565
566 if (first_then != last_then || first_else != last_else)
567 return false;
568
569 if (!nir_block_ends_in_break(last_then) &&
570 !nir_block_ends_in_break(last_else))
571 return false;
572
573 if (nir_block_ends_in_break(last_then)) {
574 if (!exec_list_is_empty(&last_else->instr_list) ||
575 !exec_list_is_singular(&last_then->instr_list))
576 return false;
577 } else {
578 assert(nir_block_ends_in_break(last_else));
579 if (!exec_list_is_empty(&last_then->instr_list) ||
580 !exec_list_is_singular(&last_else->instr_list))
581 return false;
582 }
583
584 return true;
585 }
586
587 /*
588 * Merge two consecutive loop terminators. For example:
589 *
590 * int i;
591 * for(i = 0; i < n_stop; i++) {
592 * ...
593 *
594 * if(0.0 < stops[i])
595 * break;
596 * }
597 *
598 * This loop checks if the value of stops[i] is greater than 0.0 and if untrue
599 * immediately checks n_stop is less than i. If we combine these into a single
600 * if the compiler has a greater chance of unrolling the loop.
601 */
602 static bool
opt_loop_merge_terminators(nir_builder * b,nir_if * nif,nir_loop * loop)603 opt_loop_merge_terminators(nir_builder *b, nir_if *nif, nir_loop *loop)
604 {
605 if (!loop)
606 return false;
607
608 /* If the loop has phis abort any merge attempt */
609 nir_block *blk_after_lp = nir_cf_node_cf_tree_next(&loop->cf_node);
610 nir_instr *instr_after_loop = nir_block_first_instr(blk_after_lp);
611 if (instr_after_loop && instr_after_loop->type == nir_instr_type_phi)
612 return false;
613
614 /* Check if we have two consecutive basic terminators */
615 if (!is_basic_terminator_if(nif))
616 return false;
617
618 nir_block *next_blk = nir_cf_node_cf_tree_next(&nif->cf_node);
619 if (!next_blk)
620 return false;
621
622 nir_if *next_if = nir_block_get_following_if(next_blk);
623 if (!next_if)
624 return false;
625
626 if (!is_basic_terminator_if(next_if))
627 return false;
628
629 /* If the terminators exit from different branches just abort for now.
630 * After further if-statement optimisations are done we should get another
631 * go at merging.
632 */
633 bool break_in_then_f = nir_block_ends_in_break(nir_if_last_then_block(nif));
634 bool break_in_then_s = nir_block_ends_in_break(nir_if_last_then_block(next_if));
635 if (break_in_then_f != break_in_then_s)
636 return false;
637
638 /* Allow some instructions that are acceptable between the terminators
639 * these are expected to simply be used by the condition in the second
640 * loop terminator.
641 */
642 nir_foreach_instr(instr, next_blk) {
643 if (instr->type == nir_instr_type_phi)
644 return false;
645
646 if (instr->type != nir_instr_type_alu &&
647 instr->type != nir_instr_type_load_const &&
648 instr->type != nir_instr_type_deref &&
649 (instr->type != nir_instr_type_intrinsic ||
650 (instr->type == nir_instr_type_intrinsic &&
651 nir_instr_as_intrinsic(instr)->intrinsic != nir_intrinsic_load_deref))) {
652 return false;
653 }
654 }
655
656 /* If either if-statement has phis abort */
657 next_blk = nir_cf_node_cf_tree_next(&next_if->cf_node);
658 if (next_blk) {
659 nir_foreach_instr(instr, next_blk) {
660 if (instr->type == nir_instr_type_phi)
661 return false;
662 }
663 }
664
665 merge_terminators(b, next_if, nif);
666 return true;
667 }
668
669 static bool
opt_loop_cf_list(nir_builder * b,struct exec_list * cf_list,nir_loop * current_loop)670 opt_loop_cf_list(nir_builder *b, struct exec_list *cf_list,
671 nir_loop *current_loop)
672 {
673 bool progress = false;
674 foreach_list_typed_safe(nir_cf_node, cf_node, node, cf_list) {
675 switch (cf_node->type) {
676 case nir_cf_node_block: {
677 nir_block *block = nir_cf_node_as_block(cf_node);
678 progress |= opt_loop_last_block(block, false, false);
679 break;
680 }
681
682 case nir_cf_node_if: {
683 nir_if *nif = nir_cf_node_as_if(cf_node);
684 progress |= opt_loop_cf_list(b, &nif->then_list, current_loop);
685 progress |= opt_loop_cf_list(b, &nif->else_list, current_loop);
686 progress |= opt_loop_merge_break_continue(nif);
687 progress |= opt_loop_terminator(nif);
688 progress |= opt_loop_merge_terminators(b, nif, current_loop);
689 break;
690 }
691
692 case nir_cf_node_loop: {
693 nir_loop *loop = nir_cf_node_as_loop(cf_node);
694 assert(!nir_loop_has_continue_construct(loop));
695 progress |= opt_loop_cf_list(b, &loop->body, loop);
696 progress |= opt_loop_last_block(nir_loop_last_block(loop), true, false);
697 progress |= opt_loop_peel_initial_break(loop);
698 break;
699 }
700
701 case nir_cf_node_function:
702 unreachable("Invalid cf type");
703 }
704 }
705
706 return progress;
707 }
708
709 /**
710 * This pass aims to simplify loop control-flow by reducing the number
711 * of break and continue statements.
712 */
713 bool
nir_opt_loop(nir_shader * shader)714 nir_opt_loop(nir_shader *shader)
715 {
716 bool progress = false;
717
718 nir_foreach_function_impl(impl, shader) {
719 nir_builder b = nir_builder_create(impl);
720
721 /* First we run the simple pass to get rid of pesky continues */
722 if (opt_loop_cf_list(&b, &impl->body, NULL)) {
723 nir_metadata_preserve(impl, nir_metadata_none);
724
725 /* If that made progress, we're no longer really in SSA form. */
726 nir_lower_reg_intrinsics_to_ssa_impl(impl);
727 progress = true;
728 } else {
729 nir_metadata_preserve(impl, nir_metadata_all);
730 }
731 }
732
733 return progress;
734 }
735