<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Sep 15, 2016 at 12:03 AM, Timothy Arceri <span dir="ltr"><<a href="mailto:timothy.arceri@collabora.com" target="_blank">timothy.arceri@collabora.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">V2:<br>
- tidy ups suggested by Connor.<br>
- tidy up cloning logic and handle copy propagation<br>
 based of suggestion by Connor.<br>
- use nir_ssa_def_rewrite_uses to fix up lcssa phis<br>
  suggested by Connor.<br>
- add support for complex loop unrolling (two terminators)<br>
- handle case were the ssa defs use outside the loop is already a phi<br>
- support unrolling loops with multiple terminators when trip count<br>
  is know for each terminator<br>
---<br>
 src/compiler/Makefile.sources<wbr>          |   1 +<br>
 src/compiler/nir/nir.h                 |   2 +<br>
 src/compiler/nir/nir_opt_loop<wbr>_unroll.c | 820 ++++++++++++++++++++++++++++++<wbr>+++<br>
 3 files changed, 823 insertions(+)<br>
 create mode 100644 src/compiler/nir/nir_opt_loop_<wbr>unroll.c<br>
<br>
diff --git a/src/compiler/Makefile.source<wbr>s b/src/compiler/Makefile.source<wbr>s<br>
index 8ef6080..b3512bb 100644<br>
--- a/src/compiler/Makefile.source<wbr>s<br>
+++ b/src/compiler/Makefile.source<wbr>s<br>
@@ -233,6 +233,7 @@ NIR_FILES = \<br>
        nir/nir_opt_dead_cf.c \<br>
        nir/nir_opt_gcm.c \<br>
        nir/nir_opt_global_to_local.c \<br>
+       nir/nir_opt_loop_unroll.c \<br>
        nir/nir_opt_peephole_select.c \<br>
        nir/nir_opt_remove_phis.c \<br>
        nir/nir_opt_undef.c \<br>
diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h<br>
index 9887432..0513d81 100644<br>
--- a/src/compiler/nir/nir.h<br>
+++ b/src/compiler/nir/nir.h<br>
@@ -2661,6 +2661,8 @@ bool nir_opt_dead_cf(nir_shader *shader);<br>
<br>
 bool nir_opt_gcm(nir_shader *shader, bool value_number);<br>
<br>
+bool nir_opt_loop_unroll(nir_shader *shader, nir_variable_mode indirect_mask);<br>
+<br>
 bool nir_opt_peephole_select(nir_sh<wbr>ader *shader);<br>
<br>
 bool nir_opt_remove_phis(nir_shader *shader);<br>
diff --git a/src/compiler/nir/nir_opt_loo<wbr>p_unroll.c b/src/compiler/nir/nir_opt_loo<wbr>p_unroll.c<br>
new file mode 100644<br>
index 0000000..1de02f6<br>
--- /dev/null<br>
+++ b/src/compiler/nir/nir_opt_loo<wbr>p_unroll.c<br>
@@ -0,0 +1,820 @@<br>
+/*<br>
+ * Copyright © 2016 Intel Corporation<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>
+ * DEALINGS IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#include "nir.h"<br>
+#include "nir_builder.h"<br>
+#include "nir_control_flow.h"<br>
+<br>
+static void<br>
+extract_loop_body(nir_cf_list *extracted, nir_cf_node *node)<br></blockquote><div><br></div><div>"node" is not particularly descriptive.  Perhaps "start_node" or something like that.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+{<br>
+   nir_cf_node *end = node;<br>
+   while (!nir_cf_node_is_last(end))<br>
+      end = nir_cf_node_next(end);<br></blockquote><div><br></div><div>This bit of iteration seems unfortunate.  If you have the loop pointer, you can just do<br></div><div><br></div><div>nir_cf_extract(extracted, nir_before_cf_node(node), nir_after_cf_node(nir_loop_las<wbr>t_cf_node(loop))<br><br></div><div>For that matter, is the helper even needed?  If you don't want to type that much and want to keep the helper, you could easily get the loop from node->parent.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+   nir_cf_extract(extracted, nir_before_cf_node(node),<br>
+                  nir_after_cf_node(end));<br>
+}<br>
+<br>
+static void<br>
+clone_list(nir_shader *ns, nir_loop *loop, nir_cf_list *src_cf_list,<br>
+           nir_cf_list *cloned_cf_list, struct hash_table *remap_table)<br>
+{<br>
+   /* Dest list needs to at least have one block */<br>
+   nir_block *nblk = nir_block_create(ns);<br>
+   nblk->cf_node.parent = loop->cf_node.parent;<br>
+   exec_list_push_tail(&cloned_c<wbr>f_list->list, &nblk->cf_node.node);<br></blockquote><div><br></div><div>Do you always want to do this or should it be guarded by "if (exec_list_empty(&src_cf_list-<wbr>>list))"?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+   nir_clone_loop_list(&cloned_c<wbr>f_list->list, &src_cf_list->list,<br>
+                       remap_table, ns);<br>
+}<br>
+<br>
+static void<br>
+move_cf_list_into_if(nir_cf_l<wbr>ist *lst, nir_cf_node *if_node,<br>
+                     nir_cf_node *last_node, bool continue_from_then_branch)<br>
+{<br>
+   nir_if *if_stmt = nir_cf_node_as_if(if_node);<br>
+   if (continue_from_then_branch) {<br>
+      /* Move the rest of the loop inside the then */<br>
+      nir_cf_reinsert(lst, nir_after_cf_node(nir_if_last_<wbr>then_node(if_stmt)));<br>
+   } else {<br>
+      /* Move the rest of the loop inside the else */<br>
+      nir_cf_reinsert(lst, nir_after_cf_node(nir_if_last_<wbr>else_node(if_stmt)));<br>
+   }<br>
+<br>
+   /* Remove the break */<br>
+   nir_instr_remove(nir_block_la<wbr>st_instr(nir_cf_node_as_block(<wbr>last_node)));<br>
+}<br>
+<br>
+static bool<br>
+is_phi_src_phi_from_loop_head<wbr>er(nir_ssa_def *def, nir_ssa_def *src)<br>
+{<br>
+   return def->parent_instr->type == nir_instr_type_phi &&<br>
+      src->parent_instr->type == nir_instr_type_phi &&<br>
+      nir_instr_as_phi(def->parent_i<wbr>nstr)->instr.block->index ==<br>
+      nir_instr_as_phi(src->parent_i<wbr>nstr)->instr.block->index;<br></blockquote><div><br></div><div>This last check can be shortened to "def->parent_instr->block == src->parent_instr->block".  Converting to a phi and then doing ->instr is just redundant.  Also, I'd rather compre block pointers than indices if we can.<br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+static void<br>
+get_table_of_lcssa_and_loop_t<wbr>erm_phis(nir_cf_node *loop,<br>
+                                      struct hash_table **lcssa_phis,<br>
+                                      struct hash_table **loop_term_phis,<br>
+                                      nir_if *loop_term_if)<br>
+{<br>
+   *lcssa_phis = _mesa_hash_table_create(NULL, _mesa_hash_pointer, <br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                                         _mesa_key_pointer_equal);<br>
+   *loop_term_phis = _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                             _mesa_key_pointer_equal);<br>
+<br>
+   nir_cf_node *cf_node = nir_cf_node_next(loop);<br>
+   nir_block *block = nir_cf_node_as_block(cf_node);<br>
+   nir_foreach_instr(instr, block) {<br>
+      if (instr->type == nir_instr_type_phi) {<br>
+         nir_phi_instr *phi = nir_instr_as_phi(instr);<br>
+<br>
+         nir_foreach_phi_src(src, phi) {<br>
+            nir_block *then_blk =<br>
+               nir_cf_node_as_block(nir_if_l<wbr>ast_then_node(loop_term_if));<br>
+            nir_block *else_blk =<br>
+               nir_cf_node_as_block(nir_if_l<wbr>ast_else_node(loop_term_if));<br>
+<br>
+            if (src->pred == then_blk || src->pred == else_blk) {<br>
+               _mesa_hash_table_insert(*loop<wbr>_term_phis, phi, src->src.ssa);<br>
+            } else {<br>
+               _mesa_hash_table_insert(*lcss<wbr>a_phis, phi, src->src.ssa);<br>
+            }<br>
+         }<br>
+      } else {<br>
+         /* There should be no more phis */<br>
+         break;<br>
+      }<br>
+   }<br>
+}<br>
+<br>
+static void<br>
+create_remap_tables(nir_loop *loop, nir_block *loop_header_blk,<br>
+                    struct hash_table **remap_table,<br>
+                    struct hash_table **phi_remap,<br>
+                    struct hash_table **src_before_loop,<br>
+                    struct hash_table **src_after_loop)<br>
+{<br>
+   *remap_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                          _mesa_key_pointer_equal);<br>
+   *phi_remap = _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                        _mesa_key_pointer_equal);<br>
+   *src_before_loop = _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                              _mesa_key_pointer_equal);<br>
+   *src_after_loop = _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                             _mesa_key_pointer_equal);<br>
+<br>
+   /* Build hash tables used for remapping as we unroll. */<br>
+   nir_foreach_instr(instr, loop_header_blk) {<br>
+      if (instr->type != nir_instr_type_phi)<br>
+         break;<br>
+<br>
+      nir_phi_instr *phi = nir_instr_as_phi(instr);<br>
+      nir_foreach_phi_src(src, phi) {<br>
+         /* Is the pred from the block itself? */<br></blockquote><div><br></div><div>I'm not sure what this comment is trying to say.  The logic below determines whether or not the predecessor is from some block inside the loop but not the header block.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+         if (src->pred->index > phi->instr.block->index &&<br>
+             src->pred->cf_node.parent == &loop->cf_node) {<br>
+<br>
+            _mesa_hash_table_insert(*phi_r<wbr>emap, &phi->dest.ssa, src->src.ssa);<br>
+            _mesa_hash_table_insert(*src_a<wbr>fter_loop, &phi->dest.ssa,<br>
+                                    src->src.ssa);<br>
+         } else {<br></blockquote><div><br></div><div>Perhaps assert(src->pred == start_bloc || src->pred == block_before_loop)<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+            _mesa_hash_table_insert(*remap<wbr>_table, &phi->dest.ssa,<br>
+                                    src->src.ssa);<br>
+            _mesa_hash_table_insert(*src_b<wbr>efore_loop, &phi->dest.ssa,<br>
+                                    src->src.ssa);<br>
+         }<br>
+      }<br>
+   }<br>
+}<br>
+<br>
+static void<br>
+update_remap_tables(bool is_first_iteration, struct hash_table *remap_table,<br>
+                    struct hash_table *phi_remap,<br>
+                    struct hash_table *src_before_loop,<br>
+                    struct hash_table *src_after_loop)<br>
+{<br>
+   struct hash_entry *phi_hte;<br>
+   hash_table_foreach(phi_remap, phi_hte) {<br>
+      struct hash_entry *remap_hte =<br>
+         _mesa_hash_table_search(remap<wbr>_table, phi_hte->data);<br>
+<br>
+      nir_ssa_def *phi_def = (nir_ssa_def *) phi_hte->key;<br>
+      nir_ssa_def *phi_src = (nir_ssa_def *) phi_hte->data;<br>
+<br>
+      if (!remap_hte && is_first_iteration) {<br>
+         _mesa_hash_table_insert(remap<wbr>_table, phi_hte->key, phi_hte->data);<br>
+         continue;<br>
+      }<br>
+<br>
+      if (is_phi_src_phi_from_loop_head<wbr>er(phi_def, phi_src)) {<br>
+          /* After copy propagation we can end up with phis inside loops<br>
+           * that look like this:<br>
+           *<br>
+           *    vec1 32 ssa_14 = phi block_0: ssa_9, block_4: ssa_13<br>
+           *    vec1 32 ssa_13 = phi block_0: ssa_8, block_4: ssa_12<br>
+           *    vec1 32 ssa_12 = phi block_0: ssa_7, block_4: ssa_11<br>
+           *    vec1 32 ssa_11 = phi block_0: ssa_6, block_4: ssa_14<br>
+           *<br>
+           * For each iteration of the loop we need to update the phi and<br>
+           * cloning remap tables so that we use the correct src for the<br>
+           * next iteration.<br>
+           */<br>
+          struct hash_entry *sbl_hte =<br>
+             _mesa_hash_table_search(src_b<wbr>efore_loop, phi_hte->data);<br>
+          _mesa_hash_table_insert(remap_<wbr>table, phi_hte->key, sbl_hte->data);<br>
+<br>
+          struct hash_entry *sal_hte =<br>
+             _mesa_hash_table_search(src_a<wbr>fter_loop, phi_hte->data);<br>
+          phi_hte->data = sal_hte->data;<br>
+      } else if (remap_hte) {<br>
+          _mesa_hash_table_insert(remap_<wbr>table, phi_hte->key, remap_hte->data);<br>
+      }<br></blockquote><div><br></div><div>Hrm... I understand the problem but I would have thought we could have done it with fewer remap tables...  My naeve idea would be to do as follows:<br><br></div>In create_remap_tables, populate the remap table with phi_dst -> phi_src where phi_src is the source with pred == block_before_loop.<br><br></div><div class="gmail_quote">In update_remap_tables, populate the remap table with phi_dst -> remap(phi_src) where phi_src is the soure with pred == continue_block<br><br></div><div class="gmail_quote">Am I just over-simplifying things here?<br></div><div class="gmail_quote"><br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+   }<br>
+}<br>
+<br>
+static void<br>
+insert_phi_and_set_block_on_u<wbr>ses(nir_builder *b, nir_phi_instr *phi_instr)<br>
+{<br>
+   nir_instr_insert(b->cursor, &phi_instr->instr);<br>
+<br>
+   /* Now that we have inserted the phi fix up the block for its uses. */<br>
+   nir_foreach_use_safe(use_src, &phi_instr->dest.ssa) {<br>
+      nir_phi_instr *use_phi = nir_instr_as_phi(use_src->pare<wbr>nt_instr);<br>
+<br>
+      foreach_list_typed(nir_phi_src<wbr>, src, node, &use_phi->srcs) {<br>
+         if (!src->pred)<br>
+            src->pred = phi_instr->dest.ssa.parent_ins<wbr>tr->block;<br>
+      }<br>
+   }<br>
+}<br>
+<br>
+static nir_phi_instr *<br>
+create_complex_unroll_phi(nir<wbr>_shader *ns, nir_phi_instr *prev_phi_instr)<br>
+{<br>
+   nir_phi_instr *new_phi = nir_phi_instr_create(ns);<br>
+   nir_ssa_dest_init(&new_phi->i<wbr>nstr, &new_phi->dest, 1,<br>
+                     prev_phi_instr->dest.ssa.bit_<wbr>size, NULL);<br>
+<br>
+   /* Add the new phi as a src to the phi from the previous iteration */<br>
+   nir_phi_src *new_src = ralloc(prev_phi_instr, nir_phi_src);<br>
+   new_src->src = nir_src_for_ssa(&new_phi->dest<wbr>.ssa);<br>
+   new_src->src.parent_instr = &prev_phi_instr->instr;<br>
+   exec_list_push_tail(&prev_phi<wbr>_instr->srcs, &new_src->node);<br>
+   list_addtail(&new_src-><a href="http://src.us" target="_blank">src.us</a><wbr>e_link, &new_src->src.ssa->uses);<br>
+<br>
+   return new_phi;<br>
+}<br>
+<br>
+static void<br>
+add_complex_unroll_phi_src(ni<wbr>r_ssa_def *phi_src, nir_phi_instr *phi_instr,<br>
+                           struct hash_table *remap_table, nir_block *blk)<br>
+{<br>
+   struct hash_entry *hte =<br>
+      _mesa_hash_table_search(remap_<wbr>table, phi_src);<br>
+<br>
+   nir_phi_src *new_src = ralloc(phi_instr, nir_phi_src);<br>
+   nir_ssa_def *ssa_def = hte ? (nir_ssa_def *) hte->data : phi_src;<br>
+   new_src->pred = blk;<br>
+   new_src->src = nir_src_for_ssa(ssa_def);<br>
+   new_src->src.parent_instr = &phi_instr->instr;<br>
+   list_addtail(&new_src-><a href="http://src.us" target="_blank">src.us</a><wbr>e_link, &new_src->src.ssa->uses);<br>
+<br>
+   exec_list_push_tail(&phi_inst<wbr>r->srcs, &new_src->node);<br>
+}<br>
+<br>
+static void<br>
+simple_loop_fix_lcssa_phis(ni<wbr>r_cf_node *loop, struct hash_table *remap_table)<br>
+{<br>
+   nir_block *prev_block = nir_cf_node_as_block(nir_cf_no<wbr>de_prev(loop));<br>
+   nir_cf_node *cf_node = nir_cf_node_next(loop);<br>
+   assert(cf_node->type == nir_cf_node_block);<br>
+<br>
+   nir_block *block = nir_cf_node_as_block(cf_node);<br>
+   nir_foreach_instr_safe(instr, block) {<br>
+      if (instr->type == nir_instr_type_phi) {<br>
+         nir_phi_instr *phi = nir_instr_as_phi(instr);<br>
+<br>
+         nir_foreach_phi_src_safe(src, phi) {<br>
+            /* Update predecessor */<br>
+            src->pred = prev_block;<br>
+<br>
+            /* Update src */<br>
+            struct hash_entry *hte =<br>
+               _mesa_hash_table_search(remap<wbr>_table, src->src.ssa);<br>
+            assert(hte || !phi->is_lcssa_phi);<br>
+            if (hte) {<br>
+               nir_src new_src = nir_src_for_ssa((nir_ssa_def *) hte->data);<br>
+               if (phi->is_lcssa_phi || exec_list_length(&phi->srcs) == 1) {<br>
+                  nir_ssa_def_rewrite_uses(&phi-<wbr>>dest.ssa, new_src);<br>
+               } else {<br>
+                  nir_instr_rewrite_src(instr, &src->src, new_src);<br>
+               }<br>
+            } else {<br>
+               /* If a non lcssa phi now only has 1 src rewrite its uses here.<br>
+                * This avoids the src getting rewritten to an undefined def,<br>
+                * which appears to be done in nir_cf_node_remove() when<br>
+                * removing the loop.<br>
+                */<br>
+               if (exec_list_length(&phi->srcs) == 1) {<br>
+                  struct exec_node *head = exec_list_get_head(&phi->srcs)<wbr>;<br>
+                  nir_phi_src *phi_src = exec_node_data(nir_phi_src, head, node);<br>
+                  nir_ssa_def_rewrite_uses(&phi-<wbr>>dest.ssa, phi_src->src);<br>
+               }<br>
+            }<br>
+         }<br>
+         if (phi->is_lcssa_phi || exec_list_length(&phi->srcs) == 1)<br>
+            nir_instr_remove(&phi->instr);<br></blockquote><div><br></div><div>I'm not convinced that this logic is at all what we want.  It seems like what we want is more something along these lines:<br><br></div><div>nir_foreach_phi_src(src, phi) {<br></div><div>   if (src->pred != block_with_break_in_terminator)<br></div><div>      continue;<br><br></div><div>   struct hash_entry *hte = _mesa_hash_table_search(remap_table, src->src.ssa);<br></div><div>   if (hte) {<br></div><div>      nir_ssa_def_rewrite_uses(&phi->dest.ssa, nir_src_for_ssa((nir_ssa_def *)hte->data));<br></div><div>   } else {<br>      nir_ssa_def_rewrite_uses(&phi->dest.ssa, src->src);<br>   }<br></div><div>}<br></div><div>assert(list_is_empty(&phi->dest.ssa.uses));<br></div><div><br></div><div>What we really want for the phis after the loop is to take the value that it would have taken coming from the break.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+      } else {<br>
+         /* There should be no more LCSSA-phis */<br>
+         break;<br>
+      }<br>
+   }<br>
+}<br>
+<br>
+static bool<br>
+ends_in_break(nir_block *block)<br>
+{<br>
+   if (exec_list_is_empty(&block->in<wbr>str_list))<br>
+      return false;<br>
+<br>
+   nir_instr *instr = nir_block_last_instr(block);<br>
+   return instr->type == nir_instr_type_jump &&<br>
+      nir_instr_as_jump(instr)->type == nir_jump_break;<br>
+}<br>
+<br>
+/**<br>
+ * Unroll a loop which does not contain any jumps.  For example, if the input<br>
+ * is:<br>
+ *<br>
+ *     (loop (...) ...instrs...)<br>
+ *<br>
+ * And the iteration count is 3, the output will be:<br>
+ *<br>
+ *     ...instrs... ...instrs... ...instrs...<br>
+ */<br>
+static void<br>
+simple_unroll(nir_function *fn, nir_loop *loop, nir_builder *b)<br>
+{<br>
+   nir_shader *ns = fn->shader;<br>
+<br>
+   /* Get the loop header this contains a bunch of phis and the loops<br>
+    * conditional.<br>
+    */<br>
+   nir_cf_node *lp_header_cf_node = nir_loop_first_cf_node(loop);<br>
+   nir_block *loop_header_blk = nir_cf_node_as_block(lp_header<wbr>_cf_node);<br>
+<br>
+   struct hash_table *remap_table;<br>
+   struct hash_table *phi_remap;<br>
+   struct hash_table *src_before_loop;<br>
+   struct hash_table *src_after_loop;<br>
+   create_remap_tables(loop, loop_header_blk, &remap_table, &phi_remap,<br>
+                       &src_before_loop, &src_after_loop);<br>
+<br>
+   /* Skip over loop terminator and get the loop body. */<br>
+   nir_cf_node *if_node = &loop->info->limiting_terminat<wbr>or->nif->cf_node;<br>
+   list_for_each_entry(nir_loop_<wbr>terminator, terminator,<br>
+                       &loop->info->loop_terminator_<wbr>list, loop_terminator_link) {<br>
+       nir_cf_node *loop_node = &terminator->nif->cf_node;<br>
+<br>
+      /* Remove all but the limiting terminator as we know the other exit<br>
+       * conditions can never be met.<br>
+       */<br>
+      if (loop_node != &loop->info->limiting_terminat<wbr>or->nif->cf_node) {<br>
+         nir_cf_node_remove(loop_node)<wbr>;<br></blockquote><div><br></div><div>Can we really do this?  What happens if the terminator is<br><br>if (...) {<br>   /*stuff */<br>   break;<br>} else {<br>   /* other stuff */<br>}<br><br>Answer (from IRC): That can never happen since loop analysis will bail in that case.<br><br></div><div>Also, is it actually necessary to delete the other terminators?  Can we just delete the breaks from them and trust dead_cf to clean them up properly?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+      }<br>
+   }<br>
+<br>
+   nir_cf_node *cf_node = nir_cf_node_next(if_node);<br>
+<br>
+   /* Pluck out the loop header */<br>
+   nir_cf_list lp_header;<br>
+   nir_cf_extract(&lp_header, nir_before_cf_node(lp_header_c<wbr>f_node),<br>
+                  nir_before_cf_node(if_node));<br>
+<br>
+   /* Pluck out the loop body */<br>
+   nir_cf_list loop_body;<br>
+   extract_loop_body(&loop_body, cf_node);<br></blockquote><div><br></div><div>It might be clearer if this were<br><br></div><div>nir_cf_extract(&loop_body, nir_after_cf_node(if_node), nir_after_cf_node(nir_loop_<wbr>last_cf_node(loop)));<br><br></div><div>I'm not sure the extract_loop_body helper is actually helping you.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+   /* Clone the loop header */<br>
+   nir_cf_list cloned_header;<br>
+   exec_list_make_empty(&cloned_<wbr>header.list);<br>
+   cloned_header.impl = loop_body.impl; <br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+   clone_list(ns, loop, &lp_header, &cloned_header, remap_table);<br></blockquote><div><br></div><div>It would be cool if these 4 lines were just<br><br></div><div>nir_cf_list_clone(&lp_header, &cloned_header, remap_table);<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+   /* Insert cloned loop header before the loop */<br>
+   b->cursor = nir_before_cf_node(&loop->cf_n<wbr>ode);<br>
+   nir_cf_reinsert(&cloned_heade<wbr>r, b->cursor);<br>
+<br>
+   /* Create temp block to store the cloned loop body as we unroll */<br>
+   nir_cf_list unrolled_lp_body;<br>
+   exec_list_make_empty(&unrolle<wbr>d_lp_body.list);<br>
+   unrolled_lp_body.impl = loop_body.impl;<br>
+<br>
+   /* Clone loop header and append to the loop body */<br>
+   for (unsigned i = 0; i < loop->info->trip_count; i++) {<br>
+      /* Clone loop body */<br>
+      clone_list(ns, loop, &loop_body, &unrolled_lp_body, remap_table);<br>
+<br>
+      update_remap_tables(i == 0, remap_table, phi_remap, src_before_loop,<br>
+                          src_after_loop);<br>
+<br>
+      /* Insert unrolled loop body before the loop */<br>
+      b->cursor = nir_before_cf_node(&loop->cf_n<wbr>ode);<br>
+      nir_cf_reinsert(&unrolled_lp_b<wbr>ody, b->cursor);<br>
+<br>
+      /* Clone loop header */<br>
+      clone_list(ns, loop, &lp_header, &cloned_header, remap_table);<br>
+<br>
+      /* Insert loop header after loop body */<br>
+      b->cursor = nir_before_cf_node(&loop->cf_n<wbr>ode);<br>
+      nir_cf_reinsert(&cloned_header<wbr>, b->cursor);<br>
+   }<br>
+<br>
+   /* The loop has been unrolled so remove it. */<br>
+   simple_loop_fix_lcssa_phis(&l<wbr>oop->cf_node, remap_table);<br>
+<br>
+   /* Remove the loop */<br>
+   nir_cf_node_remove(&loop->cf_<wbr>node);<br>
+<br>
+   /* Delete the original loop body & header */<br>
+   nir_cf_delete(&lp_header);<br>
+   nir_cf_delete(&loop_body);<br>
+<br>
+   _mesa_hash_table_destroy(rema<wbr>p_table, NULL);<br>
+   _mesa_hash_table_destroy(phi_<wbr>remap, NULL);<br>
+   _mesa_hash_table_destroy(src_<wbr>before_loop, NULL);<br>
+   _mesa_hash_table_destroy(src_<wbr>after_loop, NULL);<br></blockquote><div><br></div><div>I think I would be mildly happier if you used a ralloc context rather than destroying them all manually.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+/**<br>
+ * Unroll a loop with two exists when the trip count of one of the exits is<br>
+ * unknown.  If continue_from_then_branch is true, the loop is repeated only<br>
+ * when the "then" branch of the if is taken; otherwise it is repeated only<br>
+ * when the "else" branch of the if is taken.<br>
+ *<br>
+ * For example, if the input is:<br>
+ *<br>
+ *     (loop (...)<br>
+ *      ...body...<br>
+ *      (if (cond)<br>
+ *          (...then_instrs...)<br>
+ *        (...else_instrs...)))<br>
+ *<br>
+ * And the iteration count is 3, and \c continue_from_then_branch is true,<br>
+ * then the output will be:<br>
+ *<br>
+ *     ...body...<br>
+ *     (if (cond)<br>
+ *         (...then_instrs...<br>
+ *          ...body...<br>
+ *          (if (cond)<br>
+ *              (...then_instrs...<br>
+ *               ...body...<br>
+ *               (if (cond)<br>
+ *                   (...then_instrs...)<br>
+ *                 (...else_instrs...)))<br>
+ *            (...else_instrs...)))<br>
+ *       (...else_instrs))<br>
+ */<br>
+static void<br>
+complex_unroll(nir_function *fn, nir_loop *loop, nir_builder *b,<br>
+               nir_cf_node *if_node, nir_cf_node *last_node,<br>
+               bool continue_from_then_branch, bool limiting_term_second)<br>
+{<br>
+   nir_cf_node *limiting_trm = &loop->info->limiting_terminat<wbr>or->nif->cf_node;<br>
+   nir_cf_node *lp_header_cf_node = nir_loop_first_cf_node(loop);<br>
+   nir_block *loop_header_blk = nir_cf_node_as_block(lp_header<wbr>_cf_node);<br>
+<br>
+   struct hash_table *remap_table;<br>
+   struct hash_table *phi_remap;<br>
+   struct hash_table *src_before_loop;<br>
+   struct hash_table *src_after_loop;<br>
+   create_remap_tables(loop, loop_header_blk, &remap_table, &phi_remap,<br>
+                       &src_before_loop, &src_after_loop);<br>
+<br>
+   struct hash_table *loop_phis;<br>
+   struct hash_table *loop_term_phis;<br>
+   get_table_of_lcssa_and_loop_t<wbr>erm_phis(&loop->cf_node, &loop_phis,<br>
+                                         &loop_term_phis,<br>
+                                         loop->info->limiting_terminat<wbr>or->nif);<br>
+<br>
+   if (limiting_term_second) {<br>
+      /* We need some special handling when its the second terminator causing<br>
+       * us to exit the loop for example:<br>
+       *<br>
+       *   for (int i = 0; i < uniform_lp_count; i++) {<br>
+       *      colour = vec4(0.0, 1.0, 0.0, 1.0);<br>
+       *<br>
+       *      if (i == 1)<br>
+       *         break;<br>
+       *      }<br>
+       *      ... any further code is unreachable after i == 1 ...<br>
+       *   }<br>
+       *<br>
+       * Bump the trip count by one so we actually clone something. Also<br>
+       * extract everything after the limiting terminator and insert it into<br>
+       * the branch we will continue from.<br>
+       */<br>
+      loop->info->trip_count++;<br>
+<br>
+      nir_cf_list after_lt;<br>
+      extract_loop_body(&after_lt, nir_cf_node_next(limiting_trm)<wbr>);<br>
+<br>
+      nir_if *if_stmt = loop->info->limiting_terminato<wbr>r->nif;<br>
+      nir_cf_node *last_then = nir_if_last_then_node(if_stmt)<wbr>;<br>
+      if (last_then->type == nir_cf_node_block &&<br>
+          ends_in_break(nir_cf_node_as_b<wbr>lock(last_then))) {<br>
+         move_cf_list_into_if(&after_l<wbr>t, limiting_trm, last_then, false);<br>
+      } else {<br>
+         nir_cf_node *last_else = nir_if_last_else_node(if_stmt)<wbr>;<br>
+         if (last_else->type == nir_cf_node_block &&<br>
+             ends_in_break(nir_cf_node_as_<wbr>block(last_else))) {<br>
+            move_cf_list_into_if(&after_lt<wbr>, limiting_trm, last_else, true);<br>
+         }<br>
+      }<br>
+   } else {<br>
+      /* Remove the limiting terminator.  Loop analysis will only find a<br>
+       * terminator for trival if statments (then only contains break, else<br>
+       * is empty) so its safe to remove the whole thing.<br>
+       */<br>
+      nir_cf_node_remove(limiting_tr<wbr>m);<br>
+   }<br>
+<br>
+   nir_shader *ns = fn->shader;<br>
+   struct hash_table *lcssa_phis =<br>
+      _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                              _mesa_key_pointer_equal);<br>
+<br>
+   /* Create phis to be used post-if (replacements for the post-loop phis) */<br>
+   struct hash_entry *phi_hte;<br>
+   hash_table_foreach(loop_phis, phi_hte) {<br>
+      nir_phi_instr *phi_instr = (nir_phi_instr *) phi_hte->key;<br>
+      nir_phi_instr *new_phi = create_complex_unroll_phi(ns, phi_instr);<br>
+<br>
+      nir_ssa_def *ssa_def = (nir_ssa_def *) phi_hte->data;<br>
+      _mesa_hash_table_insert(lcssa_<wbr>phis, new_phi, ssa_def);<br>
+<br>
+      /* Update loop_phis to point to the replacement phi */<br>
+      phi_hte->data = &new_phi->dest.ssa;<br>
+<br>
+      struct hash_entry *loop_term_hte =<br>
+         _mesa_hash_table_search(loop_<wbr>term_phis, phi_hte->key);<br>
+      if (loop_term_hte) {<br>
+         _mesa_hash_table_insert(loop_<wbr>term_phis, new_phi, loop_term_hte->data);<br>
+         _mesa_hash_table_remove(loop_<wbr>term_phis, loop_term_hte);<br>
+      }<br>
+   }<br>
+<br>
+   /* Move everything after the terminator we don't have a trip count for<br>
+    * inside the if.<br>
+    */<br>
+   nir_cf_list loop_end;<br>
+   extract_loop_body(&loop_end, nir_cf_node_next(if_node));<br>
+   nir_if *if_stmt = nir_cf_node_as_if(if_node);<br>
+   move_cf_list_into_if(&loop_en<wbr>d, if_node, last_node,<br>
+                        continue_from_then_branch);<br>
+<br>
+   /* Pluck out the loop body. Unlike the simple unroll pass there are no<br>
+    * breaks remaining in the loop so we do not have the concept of a loop<br>
+    * header and a loop body, instead we just extract everything.<br>
+    */<br>
+   nir_cf_list loop_body;<br>
+   extract_loop_body(&loop_body, lp_header_cf_node);<br>
+<br>
+   /* Create temp block to store the cloned loop body as we unroll */<br>
+   nir_cf_list unrolled_lp_body;<br>
+   exec_list_make_empty(&unrolle<wbr>d_lp_body.list);<br>
+   unrolled_lp_body.impl = loop_body.impl;<br>
+<br>
+   /* Set the cursor to before the loop */<br>
+   b->cursor = nir_before_cf_node(&loop->cf_n<wbr>ode);<br>
+<br>
+   nir_cf_node *continue_from_node = NULL;<br>
+   for (unsigned i = 0; i < loop->info->trip_count; i++) {<br>
+      /* Clone loop body */<br>
+      clone_list(ns, loop, &loop_body, &unrolled_lp_body, remap_table);<br>
+<br>
+      nir_cf_node *last_node =<br>
+         exec_node_data(nir_cf_node,<br>
+                        exec_list_get_tail(&unrolled_l<wbr>p_body.list), node);<br>
+      assert(last_node->type == nir_cf_node_block &&<br>
+             exec_list_is_empty(&nir_cf_no<wbr>de_as_block(last_node)->instr_<wbr>list));<br>
+<br>
+      /* Insert unrolled loop body */<br>
+      nir_cf_reinsert(&unrolled_lp_b<wbr>ody, b->cursor);<br>
+<br>
+      nir_cf_node *if_node = nir_cf_node_prev(last_node);<br>
+      assert(if_node->type == nir_cf_node_if);<br>
+      if_stmt = nir_cf_node_as_if(if_node);<br>
+<br>
+      nir_cf_node *exit_from_node;<br>
+      if (continue_from_then_branch) {<br>
+         continue_from_node = nir_if_last_then_node(if_stmt)<wbr>;<br>
+         exit_from_node = nir_if_last_else_node(if_stmt)<wbr>;<br>
+      } else {<br>
+         exit_from_node = nir_if_last_then_node(if_stmt)<wbr>;<br>
+         continue_from_node = nir_if_last_else_node(if_stmt)<wbr>;<br>
+      }<br>
+<br>
+      b->cursor = nir_after_cf_node(if_node);<br>
+      if (i < loop->info->trip_count - 1) {<br>
+         struct hash_table *tmp =<br>
+            _mesa_hash_table_create(NULL, _mesa_hash_pointer,<br>
+                                    _mesa_key_pointer_equal);<br>
+<br>
+         struct hash_entry *phi_hte;<br>
+         hash_table_foreach(lcssa_phis<wbr>, phi_hte) {<br>
+            /* Insert phi created in previous iteration */<br>
+            nir_phi_instr *phi_instr = (nir_phi_instr *) phi_hte->key;<br>
+            insert_phi_and_set_block_on_us<wbr>es(b, phi_instr);<br>
+<br>
+            nir_ssa_def *ssa_def = (nir_ssa_def *) phi_hte->data;<br>
+            add_complex_unroll_phi_src(ssa<wbr>_def, phi_instr, remap_table,<br>
+                                       nir_cf_node_as_block(exit_fro<wbr>m_node));<br>
+<br>
+            /* Create phi to be fixed up by next iteration */<br>
+            nir_phi_instr *new_phi = create_complex_unroll_phi(ns, phi_instr);<br>
+            _mesa_hash_table_insert(tmp, new_phi, ssa_def);<br>
+<br>
+            struct hash_entry *loop_term_hte =<br>
+               _mesa_hash_table_search(loop_<wbr>term_phis, phi_hte->key);<br>
+            if (loop_term_hte) {<br>
+               _mesa_hash_table_insert(loop_<wbr>term_phis, new_phi,<br>
+                                       loop_term_hte->data);<br>
+               _mesa_hash_table_remove(loop_<wbr>term_phis, loop_term_hte);<br>
+            }<br>
+         }<br>
+<br>
+         /* Now that the phis have been processed replace the table with the<br>
+          * phis to be fixed up in the next iteration.<br>
+          */<br>
+         _mesa_hash_table_destroy(lcss<wbr>a_phis, NULL);<br>
+         lcssa_phis = tmp;<br>
+      } else {<br>
+         struct hash_entry *phi_hte;<br>
+         hash_table_foreach(lcssa_phis<wbr>, phi_hte) {<br>
+            /* Insert phi created in previous iteration */<br>
+            nir_phi_instr *phi_instr = (nir_phi_instr *) phi_hte->key;<br>
+            insert_phi_and_set_block_on_us<wbr>es(b, phi_instr);<br>
+<br>
+            nir_ssa_def *ssa_def = (nir_ssa_def *) phi_hte->data;<br>
+            add_complex_unroll_phi_src(ssa<wbr>_def, phi_instr, remap_table,<br>
+                                       nir_cf_node_as_block(exit_fro<wbr>m_node));<br>
+         }<br>
+      }<br>
+<br>
+      /* Ready the remap tables for the next iteration */<br>
+      update_remap_tables(i == 0, remap_table, phi_remap, src_before_loop,<br>
+                          src_after_loop);<br>
+<br>
+      /* Set the cursor to the last if in the loop body we just unrolled ready<br>
+       * for the next iteration.<br>
+       */<br>
+      b->cursor = nir_after_cf_node(continue_fro<wbr>m_node);<br>
+   }<br>
+<br>
+   /* Now that the remap table is updated add the second src to the innermost<br>
+    * phis.<br>
+    */<br>
+   hash_table_foreach(lcssa_phis<wbr>, phi_hte) {<br>
+      nir_phi_instr *phi_instr = (nir_phi_instr *) phi_hte->key;<br>
+      nir_ssa_def *phi_src = (nir_ssa_def *) phi_hte->data;<br>
+<br>
+      assert(exec_list_length(&phi_i<wbr>nstr->srcs) == 1);<br>
+<br>
+      /* Get the src for when exiting by the loop terminator */<br>
+      struct hash_entry *loop_term_hte =<br>
+         _mesa_hash_table_search(loop_<wbr>term_phis, phi_instr);<br>
+      if (loop_term_hte)<br>
+         phi_src = (nir_ssa_def *) loop_term_hte->data;<br>
+<br>
+      add_complex_unroll_phi_src(phi<wbr>_src, phi_instr, remap_table,<br>
+                                 nir_cf_node_as_block(continue<wbr>_from_node));<br>
+   }<br>
+<br>
+   /* Rewrite the uses of the old loop phis */<br>
+   hash_table_foreach(loop_phis, phi_hte) {<br>
+      nir_phi_instr *phi_instr = (nir_phi_instr *) phi_hte->key;<br>
+<br>
+      nir_foreach_use_safe(use_src, &phi_instr->dest.ssa) {<br>
+         nir_src new_src = nir_src_for_ssa((nir_ssa_def *) phi_hte->data);<br>
+         nir_instr_rewrite_src(use_src<wbr>->parent_instr, use_src, new_src);<br>
+      }<br>
+<br>
+      nir_foreach_if_use_safe(use_sr<wbr>c, &phi_instr->dest.ssa) {<br>
+         nir_src new_src = nir_src_for_ssa((nir_ssa_def *) phi_hte->data);<br>
+         nir_if_rewrite_condition(use_<wbr>src->parent_if, new_src);<br>
+      }<br>
+   }<br>
+<br>
+   /* The loop has been unrolled so remove it. */<br>
+   nir_cf_node_remove(&loop->cf_<wbr>node);<br>
+<br>
+   /* Delete the original loop body */<br>
+   nir_cf_delete(&loop_body);<br>
+<br>
+   _mesa_hash_table_destroy(loop<wbr>_phis, NULL);<br>
+   _mesa_hash_table_destroy(loop<wbr>_term_phis, NULL);<br>
+   _mesa_hash_table_destroy(lcss<wbr>a_phis, NULL);<br>
+   _mesa_hash_table_destroy(rema<wbr>p_table, NULL);<br>
+   _mesa_hash_table_destroy(phi_<wbr>remap, NULL);<br>
+   _mesa_hash_table_destroy(src_<wbr>before_loop, NULL);<br>
+   _mesa_hash_table_destroy(src_<wbr>after_loop, NULL);<br></blockquote><div><br></div><div>ralloc context?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+static bool<br>
+process_loops(nir_cf_node *cf_node, nir_builder *b, bool *innermost_loop)<br>
+{<br>
+   bool progress = false;<br>
+   nir_loop *loop;<br>
+<br>
+   switch (cf_node->type) {<br>
+   case nir_cf_node_block:<br>
+      return progress;<br>
+   case nir_cf_node_if: {<br>
+      nir_if *if_stmt = nir_cf_node_as_if(cf_node);<br>
+      foreach_list_typed_safe(nir_cf<wbr>_node, nested_node, node, &if_stmt->then_list)<br>
+         progress |= process_loops(nested_node, b, innermost_loop);<br>
+      foreach_list_typed_safe(nir_cf<wbr>_node, nested_node, node, &if_stmt->else_list)<br>
+         progress |= process_loops(nested_node, b, innermost_loop);<br>
+      return progress;<br>
+   }<br>
+   case nir_cf_node_loop: {<br>
+      loop = nir_cf_node_as_loop(cf_node);<br>
+      foreach_list_typed_safe(nir_cf<wbr>_node, nested_node, node, &loop->body)<br>
+         progress |= process_loops(nested_node, b, innermost_loop);<br>
+      break;<br>
+   }<br>
+   default:<br>
+      unreachable("unknown cf node type");<br>
+   }<br>
+<br>
+   if (*innermost_loop) {<br>
+      nir_function *fn = nir_cf_node_get_function(&loop<wbr>->cf_node)->function;<br>
+<br>
+      /* Don't attempt to unroll outer loops or a second inner loop in<br>
+       * this pass wait until the next pass as we have altered the cf.<br>
+       */<br>
+      *innermost_loop = false;<br>
+<br>
+      if (loop->info->limiting_terminat<wbr>or == NULL) {<br>
+         return progress;<br>
+      }<br>
+<br>
+      if (is_simple_loop(fn->shader, loop->info)) {<br>
+         simple_unroll(fn, loop, b);<br>
+         progress = true;<br>
+      } else {<br>
+         /* Attempt to unroll loops with two terminators. */<br>
+         if (is_complex_loop(fn->shader, loop->info)) {<br>
+            bool first_terminator = true;<br>
+            list_for_each_entry(nir_loop_t<wbr>erminator, terminator,<br>
+                                &loop->info->loop_terminator_l<wbr>ist,<br>
+                                loop_terminator_link) {<br>
+<br>
+               nir_cf_node *if_node = &terminator->nif->cf_node;<br>
+<br>
+               if (if_node == &loop->info->limiting_terminat<wbr>or->nif->cf_node) {<br>
+                  first_terminator = false;<br>
+                  continue;<br>
+               }<br>
+<br>
+               /* If the first terminator has a trip count of zero just do a<br>
+                * simple unroll as the second terminator can never be reached.<br>
+                */<br>
+               if (loop->info->trip_count == 0 && first_terminator) {<br>
+                  simple_unroll(fn, loop, b);<br>
+                  progress = true;<br>
+                  break;<br>
+               }<br>
+<br>
+               nir_if *if_stmt = nir_cf_node_as_if(if_node);<br>
+<br>
+               /* Determine which if-statement branch, if any, ends with a<br>
+                * break. Note that since predicted_num_loop_jumps == 1, it is<br>
+                * impossible for both branches to end with a break.<br>
+                */<br>
+               nir_cf_node *last_then = nir_if_last_then_node(if_stmt)<wbr>;<br>
+               if (last_then->type == nir_cf_node_block &&<br>
+                   ends_in_break(nir_cf_node_as_<wbr>block(last_then))) {<br>
+<br>
+                  complex_unroll(fn, loop, b, if_node, last_then, false,<br>
+                                 !first_terminator);<br>
+<br>
+                  progress = true;<br>
+                  break;<br>
+               } else {<br>
+                  nir_cf_node *last_else = nir_if_last_else_node(if_stmt)<wbr>;<br>
+                  if (last_else->type == nir_cf_node_block &&<br>
+                      ends_in_break(nir_cf_node_as_b<wbr>lock(last_else))) {<br>
+<br>
+                     complex_unroll(fn, loop, b, if_node, last_else, true,<br>
+                                    !first_terminator);<br>
+<br>
+                     progress = true;<br>
+                     break;<br>
+                  }<br>
+               }<br>
+            }<br>
+         }<br>
+      }<br>
+   }<br>
+<br>
+   return progress;<br>
+}<br>
+<br>
+static bool<br>
+nir_opt_loop_unroll_impl(nir_<wbr>function_impl *impl,<br>
+                         nir_variable_mode indirect_mask)<br>
+{<br>
+   bool progress = false;<br>
+   nir_metadata_require(impl, nir_metadata_loop_analysis, indirect_mask);<br>
+<br>
+   nir_builder b;<br>
+   nir_builder_init(&b, impl);<br>
+<br>
+   foreach_list_typed_safe(nir_c<wbr>f_node, node, node, &impl->body) {<br>
+      bool innermost_loop = true;<br>
+      progress |= process_loops(node, &b, &innermost_loop);<br>
+   }<br>
+<br>
+   return progress;<br>
+}<br>
+<br>
+bool<br>
+nir_opt_loop_unroll(nir_shade<wbr>r *shader, nir_variable_mode indirect_mask)<br>
+{<br>
+   bool progress = false;<br>
+<br>
+   nir_foreach_function(function<wbr>, shader) {<br>
+      if (function->impl) {<br>
+         progress |= nir_opt_loop_unroll_impl(funct<wbr>ion->impl, indirect_mask);<br>
+      }<br>
+   }<br>
+   return false;<br>
+}<br>
<span><font color="#888888">--<br>
2.7.4<br>
<br>
______________________________<wbr>_________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org" target="_blank">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div><br></div></div>