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