nir/live_variables: Use a worklist
authorJason Ekstrand <jason.ekstrand@intel.com>
Fri, 19 Dec 2014 19:49:58 +0000 (11:49 -0800)
committerJason Ekstrand <jason.ekstrand@intel.com>
Fri, 16 Jan 2015 00:54:21 +0000 (16:54 -0800)
This is a rework of the liveness algorithm using a worklist as suggested by
Connor.  Doing so reduces the number of times we walk over the instructions
because we don't have to do an entire pointless walk over the instructions
just to figure out it's time to stop.  Also, the stuff after the last loop
in the funciton will only ever get visited once.

Reviewed-by: Connor Abbott <cwabbott0@gmail.com>
src/glsl/nir/nir_live_variables.c

index 305c26476e9de408a01a0f038e2d1f0a76f37a3e..f110c5e47ab959ef688185b57fcfb82302e570aa 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include "nir.h"
+#include "nir_worklist.h"
 
 /*
  * Basic liveness analysis.  This works only in SSA form.
@@ -43,7 +44,8 @@
 struct live_variables_state {
    unsigned num_ssa_defs;
    unsigned bitset_words;
-   bool progress;
+
+   nir_block_worklist worklist;
 };
 
 static bool
@@ -68,6 +70,9 @@ index_ssa_definitions_block(nir_block *block, void *state)
    return true;
 }
 
+/* Initialize the liveness data to zero and add the given block to the
+ * worklist.
+ */
 static bool
 init_liveness_block(nir_block *block, void *void_state)
 {
@@ -81,6 +86,8 @@ init_liveness_block(nir_block *block, void *void_state)
                               state->bitset_words);
    memset(block->live_out, 0, state->bitset_words * sizeof(BITSET_WORD));
 
+   nir_block_worklist_push_head(&state->worklist, block);
+
    return true;
 }
 
@@ -110,12 +117,16 @@ set_ssa_def_dead(nir_ssa_def *def, void *void_live)
    return true;
 }
 
-/* Phi nodes exist "between" blocks and all the phi nodes at the start of a
+/** Propagates the live in of succ across the edge to the live out of pred
+ *
+ * Phi nodes exist "between" blocks and all the phi nodes at the start of a
  * block act "in parallel".  When we propagate from the live_in of one
  * block to the live out of the other, we have to kill any writes from phis
  * and make live any sources.
+ *
+ * Returns true if updating live out of pred added anything
  */
-static void
+static bool
 propagate_across_edge(nir_block *pred, nir_block *succ,
                       struct live_variables_state *state)
 {
@@ -144,44 +155,81 @@ propagate_across_edge(nir_block *pred, nir_block *succ,
       }
    }
 
+   BITSET_WORD progress = 0;
    for (unsigned i = 0; i < state->bitset_words; ++i) {
-      state->progress = state->progress || (live[i] & ~pred->live_out[i]) != 0;
+      progress |= live[i] & ~pred->live_out[i];
       pred->live_out[i] |= live[i];
    }
+   return progress != 0;
 }
 
-static bool
-walk_instructions_block(nir_block *block, void *void_state)
+void
+nir_live_variables_impl(nir_function_impl *impl)
 {
-   struct live_variables_state *state = void_state;
+   struct live_variables_state state;
 
-   /* The live out is the union (modulo phi nodes) of the live ins of its
-    * successors */
-   if (block->successors[0])
-      propagate_across_edge(block, block->successors[0], state);
-   if (block->successors[1])
-      propagate_across_edge(block, block->successors[1], state);
+   /* We start at 1 because we reserve the index value of 0 for ssa_undef
+    * instructions.  Those are never live, so their liveness information
+    * can be compacted into a single bit.
+    */
+   state.num_ssa_defs = 1;
+   nir_foreach_block(impl, index_ssa_definitions_block, &state);
 
-   memcpy(block->live_in, block->live_out,
-          state->bitset_words * sizeof(BITSET_WORD));
+   nir_block_worklist_init(&state.worklist, impl->num_blocks, NULL);
 
-   nir_if *following_if = nir_block_get_following_if(block);
-   if (following_if)
-      set_src_live(&following_if->condition, block->live_in);
+   /* We now know how many unique ssa definitions we have and we can go
+    * ahead and allocate live_in and live_out sets and add all of the
+    * blocks to the worklist.
+    */
+   state.bitset_words = BITSET_WORDS(state.num_ssa_defs);
+   nir_foreach_block(impl, init_liveness_block, &state);
 
-   nir_foreach_instr_reverse(block, instr) {
-      /* Phi nodes are handled seperately so we want to skip them.  Since
-       * we are going backwards and they are at the beginning, we can just
-       * break as soon as we see one.
+   /* We're now ready to work through the worklist and update the liveness
+    * sets of each of the blocks.  By the time we get to this point, every
+    * block in the function implementation has been pushed onto the
+    * worklist in reverse order.  As long as we keep the worklist
+    * up-to-date as we go, everything will get covered.
+    */
+   while (!nir_block_worklist_is_empty(&state.worklist)) {
+      /* We pop them off in the reverse order we pushed them on.  This way
+       * the first walk of the instructions is backwards so we only walk
+       * once in the case of no control flow.
        */
-      if (instr->type == nir_instr_type_phi)
-         break;
+      nir_block *block = nir_block_worklist_pop_head(&state.worklist);
+
+      memcpy(block->live_in, block->live_out,
+             state.bitset_words * sizeof(BITSET_WORD));
 
-      nir_foreach_ssa_def(instr, set_ssa_def_dead, block->live_in);
-      nir_foreach_src(instr, set_src_live, block->live_in);
+      nir_if *following_if = nir_block_get_following_if(block);
+      if (following_if)
+         set_src_live(&following_if->condition, block->live_in);
+
+      nir_foreach_instr_reverse(block, instr) {
+         /* Phi nodes are handled seperately so we want to skip them.  Since
+          * we are going backwards and they are at the beginning, we can just
+          * break as soon as we see one.
+          */
+         if (instr->type == nir_instr_type_phi)
+            break;
+
+         nir_foreach_ssa_def(instr, set_ssa_def_dead, block->live_in);
+         nir_foreach_src(instr, set_src_live, block->live_in);
+      }
+
+      /* Walk over all of the predecessors of the current block updating
+       * their live in with the live out of this one.  If anything has
+       * changed, add the predecessor to the work list so that we ensure
+       * that the new information is used.
+       */
+      struct set_entry *entry;
+      set_foreach(block->predecessors, entry) {
+         nir_block *pred = (nir_block *)entry->key;
+         if (propagate_across_edge(pred, block, &state))
+            nir_block_worklist_push_tail(&state.worklist, pred);
+      }
    }
 
-   return true;
+   nir_block_worklist_fini(&state.worklist);
 }
 
 static bool
@@ -246,31 +294,3 @@ nir_ssa_defs_interfere(nir_ssa_def *a, nir_ssa_def *b)
       return nir_ssa_def_is_live_at(b, a->parent_instr);
    }
 }
-
-void
-nir_live_variables_impl(nir_function_impl *impl)
-{
-   struct live_variables_state state;
-
-   /* We start at 1 because we reserve the index value of 0 for ssa_undef
-    * instructions.  Those are never live, so their liveness information
-    * can be compacted into a single bit.
-    */
-   state.num_ssa_defs = 1;
-   nir_foreach_block(impl, index_ssa_definitions_block, &state);
-
-   /* We now know how many unique ssa definitions we have and we can go
-    * ahead and allocate live_in and live_out sets
-    */
-   state.bitset_words = BITSET_WORDS(state.num_ssa_defs);
-   nir_foreach_block(impl, init_liveness_block, &state);
-
-   /* We need to propagate the liveness back through the CFG.  Thanks to
-    * the wonders of SSA, this will run no more times than the depth of the
-    * deepest loop + 1.
-    */
-   do {
-      state.progress = false;
-      nir_foreach_block_reverse(impl, walk_instructions_block, &state);
-   } while (state.progress);
-}