1/* 2 * Copyright © 2020 Julian Winkler 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24#include "nir.h" 25#include "nir_builder.h" 26#include "nir_vla.h" 27 28#define NIR_LOWER_GOTO_IFS_DEBUG 0 29 30struct path { 31 /** Set of blocks which this path represents 32 * 33 * It's "reachable" not in the sense that these are all the nodes reachable 34 * through this path but in the sense that, when you see one of these 35 * blocks, you know you've reached this path. 36 */ 37 struct set *reachable; 38 39 /** Fork in the path, if reachable->entries > 1 */ 40 struct path_fork *fork; 41}; 42 43struct path_fork { 44 bool is_var; 45 union { 46 nir_variable *path_var; 47 nir_ssa_def *path_ssa; 48 }; 49 struct path paths[2]; 50}; 51 52struct routes { 53 struct path regular; 54 struct path brk; 55 struct path cont; 56 struct routes *loop_backup; 57}; 58 59struct strct_lvl { 60 struct list_head link; 61 62 /** Set of blocks at the current level */ 63 struct set *blocks; 64 65 /** Path for the next level */ 66 struct path out_path; 67 68 /** Reach set from inside_outside if irreducable */ 69 struct set *reach; 70 71 /** True if a skip region starts with this level */ 72 bool skip_start; 73 74 /** True if a skip region ends with this level */ 75 bool skip_end; 76 77 /** True if this level is irreducable */ 78 bool irreducible; 79}; 80 81static int 82nir_block_ptr_cmp(const void *_a, const void *_b) 83{ 84 const nir_block *const *a = _a; 85 const nir_block *const *b = _b; 86 return (int)(*a)->index - (int)(*b)->index; 87} 88 89static void 90print_block_set(const struct set *set) 91{ 92 printf("{ "); 93 if (set != NULL) { 94 unsigned count = 0; 95 set_foreach(set, entry) { 96 if (count++) 97 printf(", "); 98 printf("%u", ((nir_block *)entry->key)->index); 99 } 100 } 101 printf(" }\n"); 102} 103 104/** Return a sorted array of blocks for a set 105 * 106 * Hash set ordering is non-deterministic. We hash based on pointers and so, 107 * if any pointer ever changes from one run to another, the order of the set 108 * may change. Any time we're going to make decisions which may affect the 109 * final structure which may depend on ordering, we should first sort the 110 * blocks. 111 */ 112static nir_block ** 113sorted_block_arr_for_set(const struct set *block_set, void *mem_ctx) 114{ 115 const unsigned num_blocks = block_set->entries; 116 nir_block **block_arr = ralloc_array(mem_ctx, nir_block *, num_blocks); 117 unsigned i = 0; 118 set_foreach(block_set, entry) 119 block_arr[i++] = (nir_block *)entry->key; 120 assert(i == num_blocks); 121 qsort(block_arr, num_blocks, sizeof(*block_arr), nir_block_ptr_cmp); 122 return block_arr; 123} 124 125static nir_block * 126block_for_singular_set(const struct set *block_set) 127{ 128 assert(block_set->entries == 1); 129 return (nir_block *)_mesa_set_next_entry(block_set, NULL)->key; 130} 131 132/** 133 * Sets all path variables to reach the target block via a fork 134 */ 135static void 136set_path_vars(nir_builder *b, struct path_fork *fork, nir_block *target) 137{ 138 while (fork) { 139 for (int i = 0; i < 2; i++) { 140 if (_mesa_set_search(fork->paths[i].reachable, target)) { 141 if (fork->is_var) { 142 nir_store_var(b, fork->path_var, nir_imm_bool(b, i), 1); 143 } else { 144 assert(fork->path_ssa == NULL); 145 fork->path_ssa = nir_imm_bool(b, i); 146 } 147 fork = fork->paths[i].fork; 148 break; 149 } 150 } 151 } 152} 153 154/** 155 * Sets all path variables to reach the both target blocks via a fork. 156 * If the blocks are in different fork paths, the condition will be used. 157 * As the fork is already created, the then and else blocks may be swapped, 158 * in this case the condition is inverted 159 */ 160static void 161set_path_vars_cond(nir_builder *b, struct path_fork *fork, nir_src condition, 162 nir_block *then_block, nir_block *else_block) 163{ 164 int i; 165 while (fork) { 166 for (i = 0; i < 2; i++) { 167 if (_mesa_set_search(fork->paths[i].reachable, then_block)) { 168 if (_mesa_set_search(fork->paths[i].reachable, else_block)) { 169 if (fork->is_var) { 170 nir_store_var(b, fork->path_var, nir_imm_bool(b, i), 1); 171 } else { 172 assert(fork->path_ssa == NULL); 173 fork->path_ssa = nir_imm_bool(b, i); 174 } 175 fork = fork->paths[i].fork; 176 break; 177 } 178 else { 179 assert(condition.is_ssa); 180 nir_ssa_def *ssa_def = condition.ssa; 181 assert(ssa_def->bit_size == 1); 182 assert(ssa_def->num_components == 1); 183 if (!i) 184 ssa_def = nir_inot(b, ssa_def); 185 if (fork->is_var) { 186 nir_store_var(b, fork->path_var, ssa_def, 1); 187 } else { 188 assert(fork->path_ssa == NULL); 189 fork->path_ssa = ssa_def; 190 } 191 set_path_vars(b, fork->paths[i].fork, then_block); 192 set_path_vars(b, fork->paths[!i].fork, else_block); 193 return; 194 } 195 } 196 } 197 assert(i < 2); 198 } 199} 200 201/** 202 * Sets all path variables and places the right jump instruction to reach the 203 * target block 204 */ 205static void 206route_to(nir_builder *b, struct routes *routing, nir_block *target) 207{ 208 if (_mesa_set_search(routing->regular.reachable, target)) { 209 set_path_vars(b, routing->regular.fork, target); 210 } 211 else if (_mesa_set_search(routing->brk.reachable, target)) { 212 set_path_vars(b, routing->brk.fork, target); 213 nir_jump(b, nir_jump_break); 214 } 215 else if (_mesa_set_search(routing->cont.reachable, target)) { 216 set_path_vars(b, routing->cont.fork, target); 217 nir_jump(b, nir_jump_continue); 218 } 219 else { 220 assert(!target->successors[0]); /* target is endblock */ 221 nir_jump(b, nir_jump_return); 222 } 223} 224 225/** 226 * Sets path vars and places the right jump instr to reach one of the two 227 * target blocks based on the condition. If the targets need different jump 228 * istructions, they will be placed into an if else statement. 229 * This can happen if one target is the loop head 230 * A __ 231 * | \ 232 * B | 233 * |\__/ 234 * C 235 */ 236static void 237route_to_cond(nir_builder *b, struct routes *routing, nir_src condition, 238 nir_block *then_block, nir_block *else_block) 239{ 240 if (_mesa_set_search(routing->regular.reachable, then_block)) { 241 if (_mesa_set_search(routing->regular.reachable, else_block)) { 242 set_path_vars_cond(b, routing->regular.fork, condition, 243 then_block, else_block); 244 return; 245 } 246 } else if (_mesa_set_search(routing->brk.reachable, then_block)) { 247 if (_mesa_set_search(routing->brk.reachable, else_block)) { 248 set_path_vars_cond(b, routing->brk.fork, condition, 249 then_block, else_block); 250 nir_jump(b, nir_jump_break); 251 return; 252 } 253 } else if (_mesa_set_search(routing->cont.reachable, then_block)) { 254 if (_mesa_set_search(routing->cont.reachable, else_block)) { 255 set_path_vars_cond(b, routing->cont.fork, condition, 256 then_block, else_block); 257 nir_jump(b, nir_jump_continue); 258 return; 259 } 260 } 261 262 /* then and else blocks are in different routes */ 263 nir_push_if_src(b, condition); 264 route_to(b, routing, then_block); 265 nir_push_else(b, NULL); 266 route_to(b, routing, else_block); 267 nir_pop_if(b, NULL); 268} 269 270/** 271 * Merges the reachable sets of both fork subpaths into the forks entire 272 * reachable set 273 */ 274static struct set * 275fork_reachable(struct path_fork *fork) 276{ 277 struct set *reachable = _mesa_set_clone(fork->paths[0].reachable, fork); 278 set_foreach(fork->paths[1].reachable, entry) 279 _mesa_set_add_pre_hashed(reachable, entry->hash, entry->key); 280 return reachable; 281} 282 283/** 284 * Modifies the routing to be the routing inside a loop. The old regular path 285 * becomes the new break path. The loop in path becomes the new regular and 286 * continue path. 287 * The lost routing information is stacked into the loop_backup stack. 288 * Also creates helper vars for multilevel loop jumping if needed. 289 * Also calls the nir builder to build the loop 290 */ 291static void 292loop_routing_start(struct routes *routing, nir_builder *b, 293 struct path loop_path, struct set *reach, 294 void *mem_ctx) 295{ 296 if (NIR_LOWER_GOTO_IFS_DEBUG) { 297 printf("loop_routing_start:\n"); 298 printf(" reach = "); 299 print_block_set(reach); 300 printf(" loop_path.reachable = "); 301 print_block_set(loop_path.reachable); 302 printf(" routing->regular.reachable = "); 303 print_block_set(routing->regular.reachable); 304 printf(" routing->brk.reachable = "); 305 print_block_set(routing->brk.reachable); 306 printf(" routing->cont.reachable = "); 307 print_block_set(routing->cont.reachable); 308 printf("\n"); 309 } 310 311 struct routes *routing_backup = rzalloc(mem_ctx, struct routes); 312 *routing_backup = *routing; 313 bool break_needed = false; 314 bool continue_needed = false; 315 316 set_foreach(reach, entry) { 317 if (_mesa_set_search(loop_path.reachable, entry->key)) 318 continue; 319 if (_mesa_set_search(routing->regular.reachable, entry->key)) 320 continue; 321 if (_mesa_set_search(routing->brk.reachable, entry->key)) { 322 break_needed = true; 323 continue; 324 } 325 assert(_mesa_set_search(routing->cont.reachable, entry->key)); 326 continue_needed = true; 327 } 328 329 routing->brk = routing_backup->regular; 330 routing->cont = loop_path; 331 routing->regular = loop_path; 332 routing->loop_backup = routing_backup; 333 334 if (break_needed) { 335 struct path_fork *fork = rzalloc(mem_ctx, struct path_fork); 336 fork->is_var = true; 337 fork->path_var = nir_local_variable_create(b->impl, glsl_bool_type(), 338 "path_break"); 339 fork->paths[0] = routing->brk; 340 fork->paths[1] = routing_backup->brk; 341 routing->brk.fork = fork; 342 routing->brk.reachable = fork_reachable(fork); 343 } 344 if (continue_needed) { 345 struct path_fork *fork = rzalloc(mem_ctx, struct path_fork); 346 fork->is_var = true; 347 fork->path_var = nir_local_variable_create(b->impl, glsl_bool_type(), 348 "path_continue"); 349 fork->paths[0] = routing->brk; 350 fork->paths[1] = routing_backup->cont; 351 routing->brk.fork = fork; 352 routing->brk.reachable = fork_reachable(fork); 353 } 354 nir_push_loop(b); 355} 356 357/** 358 * Gets a forks condition as ssa def if the condition is inside a helper var, 359 * the variable will be read into an ssa def 360 */ 361static nir_ssa_def * 362fork_condition(nir_builder *b, struct path_fork *fork) 363{ 364 nir_ssa_def *ret; 365 if (fork->is_var) { 366 ret = nir_load_var(b, fork->path_var); 367 } 368 else 369 ret = fork->path_ssa; 370 return ret; 371} 372 373/** 374 * Restores the routing after leaving a loop based on the loop_backup stack. 375 * Also handles multi level jump helper vars if existing and calls the nir 376 * builder to pop the nir loop 377 */ 378static void 379loop_routing_end(struct routes *routing, nir_builder *b) 380{ 381 struct routes *routing_backup = routing->loop_backup; 382 assert(routing->cont.fork == routing->regular.fork); 383 assert(routing->cont.reachable == routing->regular.reachable); 384 nir_pop_loop(b, NULL); 385 if (routing->brk.fork && routing->brk.fork->paths[1].reachable == 386 routing_backup->cont.reachable) { 387 assert(!(routing->brk.fork->is_var && 388 strcmp(routing->brk.fork->path_var->name, "path_continue"))); 389 nir_push_if_src(b, nir_src_for_ssa( 390 fork_condition(b, routing->brk.fork))); 391 nir_jump(b, nir_jump_continue); 392 nir_pop_if(b, NULL); 393 routing->brk = routing->brk.fork->paths[0]; 394 } 395 if (routing->brk.fork && routing->brk.fork->paths[1].reachable == 396 routing_backup->brk.reachable) { 397 assert(!(routing->brk.fork->is_var && 398 strcmp(routing->brk.fork->path_var->name, "path_break"))); 399 nir_push_if_src(b, nir_src_for_ssa( 400 fork_condition(b, routing->brk.fork))); 401 nir_jump(b, nir_jump_break); 402 nir_pop_if(b, NULL); 403 routing->brk = routing->brk.fork->paths[0]; 404 } 405 assert(routing->brk.fork == routing_backup->regular.fork); 406 assert(routing->brk.reachable == routing_backup->regular.reachable); 407 *routing = *routing_backup; 408 ralloc_free(routing_backup); 409} 410 411/** 412 * generates a list of all blocks dominated by the loop header, but the 413 * control flow can't go back to the loop header from the block. 414 * also generates a list of all blocks that can be reached from within the 415 * loop 416 * | __ 417 * A´ \ 418 * | \ \ 419 * B C-´ 420 * / 421 * D 422 * here B and C are directly dominated by A but only C can reach back to the 423 * loop head A. B will be added to the outside set and to the reach set. 424 * \param loop_heads set of loop heads. All blocks inside the loop will be 425 * added to this set 426 * \param outside all blocks directly outside the loop will be added 427 * \param reach all blocks reachable from the loop will be added 428 */ 429static void 430inside_outside(nir_block *block, struct set *loop_heads, struct set *outside, 431 struct set *reach, struct set *brk_reachable, void *mem_ctx) 432{ 433 assert(_mesa_set_search(loop_heads, block)); 434 struct set *remaining = _mesa_pointer_set_create(mem_ctx); 435 for (int i = 0; i < block->num_dom_children; i++) { 436 if (!_mesa_set_search(brk_reachable, block->dom_children[i])) 437 _mesa_set_add(remaining, block->dom_children[i]); 438 } 439 440 441 if (NIR_LOWER_GOTO_IFS_DEBUG) { 442 printf("inside_outside(%u):\n", block->index); 443 printf(" loop_heads = "); 444 print_block_set(loop_heads); 445 printf(" reach = "); 446 print_block_set(reach); 447 printf(" brk_reach = "); 448 print_block_set(brk_reachable); 449 printf(" remaining = "); 450 print_block_set(remaining); 451 printf("\n"); 452 } 453 454 bool progress = true; 455 while (remaining->entries && progress) { 456 progress = false; 457 set_foreach(remaining, child_entry) { 458 nir_block *dom_child = (nir_block *) child_entry->key; 459 bool can_jump_back = false; 460 set_foreach(dom_child->dom_frontier, entry) { 461 if (entry->key == dom_child) 462 continue; 463 if (_mesa_set_search_pre_hashed(remaining, entry->hash, 464 entry->key)) { 465 can_jump_back = true; 466 break; 467 } 468 if (_mesa_set_search_pre_hashed(loop_heads, entry->hash, 469 entry->key)) { 470 can_jump_back = true; 471 break; 472 } 473 } 474 if (!can_jump_back) { 475 _mesa_set_add_pre_hashed(outside, child_entry->hash, 476 child_entry->key); 477 _mesa_set_remove(remaining, child_entry); 478 progress = true; 479 } 480 } 481 } 482 483 /* Add everything remaining to loop_heads */ 484 set_foreach(remaining, entry) 485 _mesa_set_add_pre_hashed(loop_heads, entry->hash, entry->key); 486 487 /* Recurse for each remaining */ 488 set_foreach(remaining, entry) { 489 inside_outside((nir_block *) entry->key, loop_heads, outside, reach, 490 brk_reachable, mem_ctx); 491 } 492 493 for (int i = 0; i < 2; i++) { 494 if (block->successors[i] && block->successors[i]->successors[0] && 495 !_mesa_set_search(loop_heads, block->successors[i])) { 496 _mesa_set_add(reach, block->successors[i]); 497 } 498 } 499 500 if (NIR_LOWER_GOTO_IFS_DEBUG) { 501 printf("outside(%u) = ", block->index); 502 print_block_set(outside); 503 printf("reach(%u) = ", block->index); 504 print_block_set(reach); 505 } 506} 507 508static struct path_fork * 509select_fork_recur(struct nir_block **blocks, unsigned start, unsigned end, 510 nir_function_impl *impl, bool need_var, void *mem_ctx) 511{ 512 if (start == end - 1) 513 return NULL; 514 515 struct path_fork *fork = rzalloc(mem_ctx, struct path_fork); 516 fork->is_var = need_var; 517 if (need_var) 518 fork->path_var = nir_local_variable_create(impl, glsl_bool_type(), 519 "path_select"); 520 521 unsigned mid = start + (end - start) / 2; 522 523 fork->paths[0].reachable = _mesa_pointer_set_create(fork); 524 for (unsigned i = start; i < mid; i++) 525 _mesa_set_add(fork->paths[0].reachable, blocks[i]); 526 fork->paths[0].fork = 527 select_fork_recur(blocks, start, mid, impl, need_var, mem_ctx); 528 529 fork->paths[1].reachable = _mesa_pointer_set_create(fork); 530 for (unsigned i = mid; i < end; i++) 531 _mesa_set_add(fork->paths[1].reachable, blocks[i]); 532 fork->paths[1].fork = 533 select_fork_recur(blocks, mid, end, impl, need_var, mem_ctx); 534 535 return fork; 536} 537 538/** 539 * Gets a set of blocks organized into the same level by the organize_levels 540 * function and creates enough forks to be able to route to them. 541 * If the set only contains one block, the function has nothing to do. 542 * The set should almost never contain more than two blocks, but if so, 543 * then the function calls itself recursively 544 */ 545static struct path_fork * 546select_fork(struct set *reachable, nir_function_impl *impl, bool need_var, 547 void *mem_ctx) 548{ 549 assert(reachable->entries > 0); 550 if (reachable->entries <= 1) 551 return NULL; 552 553 /* Hash set ordering is non-deterministic. We're about to turn a set into 554 * a tree so we really want things to be in a deterministic ordering. 555 */ 556 return select_fork_recur(sorted_block_arr_for_set(reachable, mem_ctx), 557 0, reachable->entries, impl, need_var, mem_ctx); 558} 559 560/** 561 * gets called when the organize_levels functions fails to find blocks that 562 * can't be reached by the other remaining blocks. This means, at least two 563 * dominance sibling blocks can reach each other. So we have a multi entry 564 * loop. This function tries to find the smallest possible set of blocks that 565 * must be part of the multi entry loop. 566 * example cf: | | 567 * A<---B 568 * / \__,^ \ 569 * \ / 570 * \ / 571 * C 572 * The function choses a random block as candidate. for example C 573 * The function checks which remaining blocks can reach C, in this case A. 574 * So A becomes the new candidate and C is removed from the result set. 575 * B can reach A. 576 * So B becomes the new candidate and A is removed from the set. 577 * A can reach B. 578 * A was an old candidate. So it is added to the set containing B. 579 * No other remaining blocks can reach A or B. 580 * So only A and B must be part of the multi entry loop. 581 */ 582static void 583handle_irreducible(struct set *remaining, struct strct_lvl *curr_level, 584 struct set *brk_reachable, void *mem_ctx) 585{ 586 nir_block *candidate = (nir_block *) 587 _mesa_set_next_entry(remaining, NULL)->key; 588 struct set *old_candidates = _mesa_pointer_set_create(mem_ctx); 589 while (candidate) { 590 _mesa_set_add(old_candidates, candidate); 591 592 /* Start with just the candidate block */ 593 _mesa_set_clear(curr_level->blocks, NULL); 594 _mesa_set_add(curr_level->blocks, candidate); 595 596 candidate = NULL; 597 set_foreach(remaining, entry) { 598 nir_block *remaining_block = (nir_block *) entry->key; 599 if (!_mesa_set_search(curr_level->blocks, remaining_block) && 600 _mesa_set_intersects(remaining_block->dom_frontier, 601 curr_level->blocks)) { 602 if (_mesa_set_search(old_candidates, remaining_block)) { 603 _mesa_set_add(curr_level->blocks, remaining_block); 604 } else { 605 candidate = remaining_block; 606 break; 607 } 608 } 609 } 610 } 611 _mesa_set_destroy(old_candidates, NULL); 612 old_candidates = NULL; 613 614 struct set *loop_heads = _mesa_set_clone(curr_level->blocks, curr_level); 615 curr_level->reach = _mesa_pointer_set_create(curr_level); 616 set_foreach(curr_level->blocks, entry) { 617 _mesa_set_remove_key(remaining, entry->key); 618 inside_outside((nir_block *) entry->key, loop_heads, remaining, 619 curr_level->reach, brk_reachable, mem_ctx); 620 } 621 _mesa_set_destroy(loop_heads, NULL); 622} 623 624/** 625 * organize a set of blocks into a list of levels. Where every level contains 626 * one or more blocks. So that every block is before all blocks it can reach. 627 * Also creates all path variables needed, for the control flow between the 628 * block. 629 * For example if the control flow looks like this: 630 * A 631 * / | 632 * B C 633 * | / \ 634 * E | 635 * \ / 636 * F 637 * B, C, E and F are dominance children of A 638 * The level list should look like this: 639 * blocks irreducible conditional 640 * level 0 B, C false false 641 * level 1 E false true 642 * level 2 F false false 643 * The final structure should look like this: 644 * A 645 * if (path_select) { 646 * B 647 * } else { 648 * C 649 * } 650 * if (path_conditional) { 651 * E 652 * } 653 * F 654 * 655 * \param levels uninitialized list 656 * \param is_dominated if true, no helper variables will be created for the 657 * zeroth level 658 */ 659static void 660organize_levels(struct list_head *levels, struct set *remaining, 661 struct set *reach, struct routes *routing, 662 nir_function_impl *impl, bool is_domminated, void *mem_ctx) 663{ 664 if (NIR_LOWER_GOTO_IFS_DEBUG) { 665 printf("organize_levels:\n"); 666 printf(" reach = "); 667 print_block_set(reach); 668 } 669 670 /* blocks that can be reached by the remaining blocks */ 671 struct set *remaining_frontier = _mesa_pointer_set_create(mem_ctx); 672 673 /* targets of active skip path */ 674 struct set *skip_targets = _mesa_pointer_set_create(mem_ctx); 675 676 list_inithead(levels); 677 while (remaining->entries) { 678 _mesa_set_clear(remaining_frontier, NULL); 679 set_foreach(remaining, entry) { 680 nir_block *remain_block = (nir_block *) entry->key; 681 set_foreach(remain_block->dom_frontier, frontier_entry) { 682 nir_block *frontier = (nir_block *) frontier_entry->key; 683 if (frontier != remain_block) { 684 _mesa_set_add(remaining_frontier, frontier); 685 } 686 } 687 } 688 689 struct strct_lvl *curr_level = rzalloc(mem_ctx, struct strct_lvl); 690 curr_level->blocks = _mesa_pointer_set_create(curr_level); 691 set_foreach(remaining, entry) { 692 nir_block *candidate = (nir_block *) entry->key; 693 if (!_mesa_set_search(remaining_frontier, candidate)) { 694 _mesa_set_add(curr_level->blocks, candidate); 695 _mesa_set_remove_key(remaining, candidate); 696 } 697 } 698 699 curr_level->irreducible = !curr_level->blocks->entries; 700 if (curr_level->irreducible) { 701 handle_irreducible(remaining, curr_level, 702 routing->brk.reachable, mem_ctx); 703 } 704 assert(curr_level->blocks->entries); 705 706 struct strct_lvl *prev_level = NULL; 707 if (!list_is_empty(levels)) 708 prev_level = list_last_entry(levels, struct strct_lvl, link); 709 710 set_foreach(skip_targets, entry) { 711 if (_mesa_set_search_pre_hashed(curr_level->blocks, 712 entry->hash, entry->key)) { 713 _mesa_set_remove(skip_targets, entry); 714 prev_level->skip_end = 1; 715 } 716 } 717 curr_level->skip_start = skip_targets->entries != 0; 718 719 struct set *prev_frontier = NULL; 720 if (!prev_level) { 721 prev_frontier = _mesa_set_clone(reach, curr_level); 722 } else if (prev_level->irreducible) { 723 prev_frontier = _mesa_set_clone(prev_level->reach, curr_level); 724 } 725 726 set_foreach(curr_level->blocks, blocks_entry) { 727 nir_block *level_block = (nir_block *) blocks_entry->key; 728 if (prev_frontier == NULL) { 729 prev_frontier = 730 _mesa_set_clone(level_block->dom_frontier, curr_level); 731 } else { 732 set_foreach(level_block->dom_frontier, entry) 733 _mesa_set_add_pre_hashed(prev_frontier, entry->hash, 734 entry->key); 735 } 736 } 737 738 bool is_in_skip = skip_targets->entries != 0; 739 set_foreach(prev_frontier, entry) { 740 if (_mesa_set_search(remaining, entry->key) || 741 (_mesa_set_search(routing->regular.reachable, entry->key) && 742 !_mesa_set_search(routing->brk.reachable, entry->key) && 743 !_mesa_set_search(routing->cont.reachable, entry->key))) { 744 _mesa_set_add_pre_hashed(skip_targets, entry->hash, entry->key); 745 if (is_in_skip) 746 prev_level->skip_end = 1; 747 curr_level->skip_start = 1; 748 } 749 } 750 751 curr_level->skip_end = 0; 752 list_addtail(&curr_level->link, levels); 753 } 754 755 if (NIR_LOWER_GOTO_IFS_DEBUG) { 756 printf(" levels:\n"); 757 list_for_each_entry(struct strct_lvl, level, levels, link) { 758 printf(" "); 759 print_block_set(level->blocks); 760 } 761 printf("\n"); 762 } 763 764 if (skip_targets->entries) 765 list_last_entry(levels, struct strct_lvl, link)->skip_end = 1; 766 767 /* Iterate throught all levels reverse and create all the paths and forks */ 768 struct path path_after_skip; 769 770 list_for_each_entry_rev(struct strct_lvl, level, levels, link) { 771 bool need_var = !(is_domminated && level->link.prev == levels); 772 level->out_path = routing->regular; 773 if (level->skip_end) { 774 path_after_skip = routing->regular; 775 } 776 routing->regular.reachable = level->blocks; 777 routing->regular.fork = select_fork(routing->regular.reachable, impl, 778 need_var, mem_ctx); 779 if (level->skip_start) { 780 struct path_fork *fork = rzalloc(mem_ctx, struct path_fork); 781 fork->is_var = need_var; 782 if (need_var) 783 fork->path_var = nir_local_variable_create(impl, glsl_bool_type(), 784 "path_conditional"); 785 fork->paths[0] = path_after_skip; 786 fork->paths[1] = routing->regular; 787 routing->regular.fork = fork; 788 routing->regular.reachable = fork_reachable(fork); 789 } 790 } 791} 792 793static void 794nir_structurize(struct routes *routing, nir_builder *b, 795 nir_block *block, void *mem_ctx); 796 797/** 798 * Places all the if else statements to select between all blocks in a select 799 * path 800 */ 801static void 802select_blocks(struct routes *routing, nir_builder *b, 803 struct path in_path, void *mem_ctx) 804{ 805 if (!in_path.fork) { 806 nir_block *block = block_for_singular_set(in_path.reachable); 807 nir_structurize(routing, b, block, mem_ctx); 808 } else { 809 assert(!(in_path.fork->is_var && 810 strcmp(in_path.fork->path_var->name, "path_select"))); 811 nir_push_if_src(b, nir_src_for_ssa(fork_condition(b, in_path.fork))); 812 select_blocks(routing, b, in_path.fork->paths[1], mem_ctx); 813 nir_push_else(b, NULL); 814 select_blocks(routing, b, in_path.fork->paths[0], mem_ctx); 815 nir_pop_if(b, NULL); 816 } 817} 818 819/** 820 * Builds the structurized nir code by the final level list. 821 */ 822static void 823plant_levels(struct list_head *levels, struct routes *routing, 824 nir_builder *b, void *mem_ctx) 825{ 826 /* Place all dominated blocks and build the path forks */ 827 list_for_each_entry(struct strct_lvl, level, levels, link) { 828 if (level->skip_start) { 829 assert(routing->regular.fork); 830 assert(!(routing->regular.fork->is_var && strcmp( 831 routing->regular.fork->path_var->name, "path_conditional"))); 832 nir_push_if_src(b, nir_src_for_ssa( 833 fork_condition(b, routing->regular.fork))); 834 routing->regular = routing->regular.fork->paths[1]; 835 } 836 struct path in_path = routing->regular; 837 routing->regular = level->out_path; 838 if (level->irreducible) 839 loop_routing_start(routing, b, in_path, level->reach, mem_ctx); 840 select_blocks(routing, b, in_path, mem_ctx); 841 if (level->irreducible) 842 loop_routing_end(routing, b); 843 if (level->skip_end) 844 nir_pop_if(b, NULL); 845 } 846} 847 848/** 849 * builds the control flow of a block and all its dominance children 850 * \param routing the routing after the block and all dominated blocks 851 */ 852static void 853nir_structurize(struct routes *routing, nir_builder *b, nir_block *block, 854 void *mem_ctx) 855{ 856 struct set *remaining = _mesa_pointer_set_create(mem_ctx); 857 for (int i = 0; i < block->num_dom_children; i++) { 858 if (!_mesa_set_search(routing->brk.reachable, block->dom_children[i])) 859 _mesa_set_add(remaining, block->dom_children[i]); 860 } 861 862 /* If the block can reach back to itself, it is a loop head */ 863 int is_looped = _mesa_set_search(block->dom_frontier, block) != NULL; 864 struct list_head outside_levels; 865 if (is_looped) { 866 struct set *loop_heads = _mesa_pointer_set_create(mem_ctx); 867 _mesa_set_add(loop_heads, block); 868 869 struct set *outside = _mesa_pointer_set_create(mem_ctx); 870 struct set *reach = _mesa_pointer_set_create(mem_ctx); 871 inside_outside(block, loop_heads, outside, reach, 872 routing->brk.reachable, mem_ctx); 873 874 set_foreach(outside, entry) 875 _mesa_set_remove_key(remaining, entry->key); 876 877 organize_levels(&outside_levels, outside, reach, routing, b->impl, 878 false, mem_ctx); 879 880 struct path loop_path = { 881 .reachable = _mesa_pointer_set_create(mem_ctx), 882 .fork = NULL, 883 }; 884 _mesa_set_add(loop_path.reachable, block); 885 886 loop_routing_start(routing, b, loop_path, reach, mem_ctx); 887 } 888 889 struct set *reach = _mesa_pointer_set_create(mem_ctx); 890 if (block->successors[0]->successors[0]) /* it is not the end_block */ 891 _mesa_set_add(reach, block->successors[0]); 892 if (block->successors[1] && block->successors[1]->successors[0]) 893 _mesa_set_add(reach, block->successors[1]); 894 895 struct list_head levels; 896 organize_levels(&levels, remaining, reach, routing, b->impl, true, mem_ctx); 897 898 /* Push all instructions of this block, without the jump instr */ 899 nir_jump_instr *jump_instr = NULL; 900 nir_foreach_instr_safe(instr, block) { 901 if (instr->type == nir_instr_type_jump) { 902 jump_instr = nir_instr_as_jump(instr); 903 break; 904 } 905 nir_instr_remove(instr); 906 nir_builder_instr_insert(b, instr); 907 } 908 909 /* Find path to the successor blocks */ 910 if (jump_instr->type == nir_jump_goto_if) { 911 route_to_cond(b, routing, jump_instr->condition, 912 jump_instr->target, jump_instr->else_target); 913 } else { 914 route_to(b, routing, block->successors[0]); 915 } 916 917 plant_levels(&levels, routing, b, mem_ctx); 918 if (is_looped) { 919 loop_routing_end(routing, b); 920 plant_levels(&outside_levels, routing, b, mem_ctx); 921 } 922} 923 924static bool 925nir_lower_goto_ifs_impl(nir_function_impl *impl) 926{ 927 if (impl->structured) { 928 nir_metadata_preserve(impl, nir_metadata_all); 929 return false; 930 } 931 932 nir_metadata_require(impl, nir_metadata_dominance); 933 934 /* We're going to re-arrange blocks like crazy. This is much easier to do 935 * if we don't have any phi nodes to fix up. 936 */ 937 nir_foreach_block_unstructured(block, impl) 938 nir_lower_phis_to_regs_block(block); 939 940 nir_cf_list cf_list; 941 nir_cf_extract(&cf_list, nir_before_cf_list(&impl->body), 942 nir_after_cf_list(&impl->body)); 943 944 /* From this point on, it's structured */ 945 impl->structured = true; 946 947 nir_builder b; 948 nir_builder_init(&b, impl); 949 b.cursor = nir_before_block(nir_start_block(impl)); 950 951 void *mem_ctx = ralloc_context(b.shader); 952 953 struct set *end_set = _mesa_pointer_set_create(mem_ctx); 954 _mesa_set_add(end_set, impl->end_block); 955 struct set *empty_set = _mesa_pointer_set_create(mem_ctx); 956 957 nir_cf_node *start_node = 958 exec_node_data(nir_cf_node, exec_list_get_head(&cf_list.list), node); 959 nir_block *start_block = nir_cf_node_as_block(start_node); 960 961 struct routes *routing = rzalloc(mem_ctx, struct routes); 962 *routing = (struct routes) { 963 .regular.reachable = end_set, 964 .brk.reachable = empty_set, 965 .cont.reachable = empty_set, 966 }; 967 nir_structurize(routing, &b, start_block, mem_ctx); 968 assert(routing->regular.fork == NULL); 969 assert(routing->brk.fork == NULL); 970 assert(routing->cont.fork == NULL); 971 assert(routing->brk.reachable == empty_set); 972 assert(routing->cont.reachable == empty_set); 973 974 ralloc_free(mem_ctx); 975 nir_cf_delete(&cf_list); 976 977 nir_metadata_preserve(impl, nir_metadata_none); 978 979 nir_repair_ssa_impl(impl); 980 nir_lower_regs_to_ssa_impl(impl); 981 982 return true; 983} 984 985bool 986nir_lower_goto_ifs(nir_shader *shader) 987{ 988 bool progress = true; 989 990 nir_foreach_function(function, shader) { 991 if (function->impl && nir_lower_goto_ifs_impl(function->impl)) 992 progress = true; 993 } 994 995 return progress; 996} 997