mirror of OpenBSD xenocara tree github.com/openbsd/xenocara
openbsd
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at jcs 734 lines 24 kB view raw
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 11static bool 12is_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 18static bool 19is_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 26static bool 27nir_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 */ 69static bool 70opt_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 */ 121static bool 122opt_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 */ 208static bool 209opt_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 301static bool 302can_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 */ 362static bool 363opt_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 448struct 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 455static bool 456insert_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 504static void 505merge_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 */ 558static bool 559is_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 */ 602static bool 603opt_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 669static bool 670opt_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 */ 713bool 714nir_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}