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