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