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