~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to src/compiler/nir/nir_lower_goto_ifs.c

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

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