• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
nir_block_ptr_cmp(const void * _a,const void * _b)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
print_block_set(const struct set * set)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 **
sorted_block_arr_for_set(const struct set * block_set,void * mem_ctx)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 *
block_for_singular_set(const struct set * block_set)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
set_path_vars(nir_builder * b,struct path_fork * fork,nir_block * target)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
set_path_vars_cond(nir_builder * b,struct path_fork * fork,nir_src condition,nir_block * then_block,nir_block * else_block)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
route_to(nir_builder * b,struct routes * routing,nir_block * target)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
route_to_cond(nir_builder * b,struct routes * routing,nir_src condition,nir_block * then_block,nir_block * else_block)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 *
fork_reachable(struct path_fork * fork)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
loop_routing_start(struct routes * routing,nir_builder * b,struct path loop_path,struct set * reach,void * mem_ctx)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 *
fork_condition(nir_builder * b,struct path_fork * fork)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
loop_routing_end(struct routes * routing,nir_builder * b)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
inside_outside(nir_block * block,struct set * loop_heads,struct set * outside,struct set * reach,struct set * brk_reachable,void * mem_ctx)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 *
select_fork_recur(struct nir_block ** blocks,unsigned start,unsigned end,nir_function_impl * impl,bool need_var,void * mem_ctx)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 *
select_fork(struct set * reachable,nir_function_impl * impl,bool need_var,void * mem_ctx)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
handle_irreducible(struct set * remaining,struct strct_lvl * curr_level,struct set * brk_reachable,void * mem_ctx)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
organize_levels(struct list_head * levels,struct set * remaining,struct set * reach,struct routes * routing,nir_function_impl * impl,bool is_domminated,void * mem_ctx)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
select_blocks(struct routes * routing,nir_builder * b,struct path in_path,void * mem_ctx)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
plant_levels(struct list_head * levels,struct routes * routing,nir_builder * b,void * mem_ctx)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
nir_structurize(struct routes * routing,nir_builder * b,nir_block * block,void * mem_ctx)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
nir_lower_goto_ifs_impl(nir_function_impl * impl)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
nir_lower_goto_ifs(nir_shader * shader)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