Home | History | Annotate | Line # | Download | only in nir
      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 
     30 struct 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 
     43 struct 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 
     52 struct routes {
     53    struct path regular;
     54    struct path brk;
     55    struct path cont;
     56    struct routes *loop_backup;
     57 };
     58 
     59 struct 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 
     81 static int
     82 nir_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 
     89 static void
     90 print_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  */
    112 static nir_block **
    113 sorted_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 
    125 static nir_block *
    126 block_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  */
    135 static void
    136 set_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  */
    160 static void
    161 set_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  */
    205 static void
    206 route_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  */
    236 static void
    237 route_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  */
    274 static struct set *
    275 fork_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  */
    291 static void
    292 loop_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  */
    361 static nir_ssa_def *
    362 fork_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  */
    378 static void
    379 loop_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  */
    429 static void
    430 inside_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 
    508 static struct path_fork *
    509 select_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  */
    545 static struct path_fork *
    546 select_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  */
    582 static void
    583 handle_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  */
    659 static void
    660 organize_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 
    793 static void
    794 nir_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  */
    801 static void
    802 select_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  */
    822 static void
    823 plant_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  */
    852 static void
    853 nir_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 
    924 static bool
    925 nir_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 
    985 bool
    986 nir_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