nir: Stop using apostrophes to pluralize.
[mesa.git] / src / compiler / nir / nir_from_ssa.c
index 6d92130b8599699a4d2217ab393782964f5f013a..27e94f823b032e977168c295f9e7e71ccdafc620 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #include "nir.h"
+#include "nir_builder.h"
 #include "nir_vla.h"
 
 /*
  */
 
 struct from_ssa_state {
-   void *mem_ctx;
+   nir_builder builder;
    void *dead_ctx;
    bool phi_webs_only;
    struct hash_table *merge_node_table;
    nir_instr *instr;
-   nir_function_impl *impl;
 };
 
 /* Returns true if a dominates b */
@@ -71,7 +71,7 @@ ssa_def_dominates(nir_ssa_def *a, nir_ssa_def *b)
  * Each SSA definition is associated with a merge_node and the association
  * is represented by a combination of a hash table and the "def" parameter
  * in the merge_node structure.  The merge_set stores a linked list of
- * merge_node's in dominence order of the ssa definitions.  (Since the
+ * merge_nodes in dominence order of the ssa definitions.  (Since the
  * liveness analysis pass indexes the SSA values in dominence order for us,
  * this is an easy thing to keep up.)  It is assumed that no pair of the
  * nodes in a given set interfere.  Merging two sets or checking for
@@ -305,7 +305,7 @@ static bool
 isolate_phi_nodes_block(nir_block *block, void *dead_ctx)
 {
    nir_instr *last_phi_instr = NULL;
-   nir_foreach_instr(block, instr) {
+   nir_foreach_instr(instr, block) {
       /* Phi nodes only ever come at the start of a block */
       if (instr->type != nir_instr_type_phi)
          break;
@@ -313,7 +313,7 @@ isolate_phi_nodes_block(nir_block *block, void *dead_ctx)
       last_phi_instr = instr;
    }
 
-   /* If we don't have any phi's, then there's nothing for us to do. */
+   /* If we don't have any phis, then there's nothing for us to do. */
    if (last_phi_instr == NULL)
       return true;
 
@@ -324,14 +324,14 @@ isolate_phi_nodes_block(nir_block *block, void *dead_ctx)
       nir_parallel_copy_instr_create(dead_ctx);
    nir_instr_insert_after(last_phi_instr, &block_pcopy->instr);
 
-   nir_foreach_instr(block, instr) {
+   nir_foreach_instr(instr, block) {
       /* Phi nodes only ever come at the start of a block */
       if (instr->type != nir_instr_type_phi)
          break;
 
       nir_phi_instr *phi = nir_instr_as_phi(instr);
       assert(phi->dest.is_ssa);
-      nir_foreach_phi_src(phi, src) {
+      nir_foreach_phi_src(src, phi) {
          nir_parallel_copy_instr *pcopy =
             get_parallel_copy_at_end_of_block(src->pred);
          assert(pcopy);
@@ -370,7 +370,7 @@ isolate_phi_nodes_block(nir_block *block, void *dead_ctx)
 static bool
 coalesce_phi_nodes_block(nir_block *block, struct from_ssa_state *state)
 {
-   nir_foreach_instr(block, instr) {
+   nir_foreach_instr(instr, block) {
       /* Phi nodes only ever come at the start of a block */
       if (instr->type != nir_instr_type_phi)
          break;
@@ -380,7 +380,7 @@ coalesce_phi_nodes_block(nir_block *block, struct from_ssa_state *state)
       assert(phi->dest.is_ssa);
       merge_node *dest_node = get_merge_node(&phi->dest.ssa, state);
 
-      nir_foreach_phi_src(phi, src) {
+      nir_foreach_phi_src(src, phi) {
          assert(src->src.is_ssa);
          merge_node *src_node = get_merge_node(src->src.ssa, state);
          if (src_node->set != dest_node->set)
@@ -395,7 +395,7 @@ static void
 aggressive_coalesce_parallel_copy(nir_parallel_copy_instr *pcopy,
                                  struct from_ssa_state *state)
 {
-   nir_foreach_parallel_copy_entry(pcopy, entry) {
+   nir_foreach_parallel_copy_entry(entry, pcopy) {
       if (!entry->src.is_ssa)
          continue;
 
@@ -424,7 +424,7 @@ static bool
 aggressive_coalesce_block(nir_block *block, struct from_ssa_state *state)
 {
    nir_parallel_copy_instr *start_pcopy = NULL;
-   nir_foreach_instr(block, instr) {
+   nir_foreach_instr(instr, block) {
       /* Phi nodes only ever come at the start of a block */
       if (instr->type != nir_instr_type_phi) {
          if (instr->type != nir_instr_type_parallel_copy)
@@ -447,6 +447,19 @@ aggressive_coalesce_block(nir_block *block, struct from_ssa_state *state)
    return true;
 }
 
+static nir_register *
+create_reg_for_ssa_def(nir_ssa_def *def, nir_function_impl *impl)
+{
+   nir_register *reg = nir_local_reg_create(impl);
+
+   reg->name = def->name;
+   reg->num_components = def->num_components;
+   reg->bit_size = def->bit_size;
+   reg->num_array_elems = 0;
+
+   return reg;
+}
+
 static bool
 rewrite_ssa_def(nir_ssa_def *def, void *void_state)
 {
@@ -463,13 +476,8 @@ rewrite_ssa_def(nir_ssa_def *def, void *void_state)
        * the things in the merge set should be the same so it doesn't
        * matter which node's definition we use.
        */
-      if (node->set->reg == NULL) {
-         node->set->reg = nir_local_reg_create(state->impl);
-         node->set->reg->name = def->name;
-         node->set->reg->num_components = def->num_components;
-         node->set->reg->bit_size = def->bit_size;
-         node->set->reg->num_array_elems = 0;
-      }
+      if (node->set->reg == NULL)
+         node->set->reg = create_reg_for_ssa_def(def, state->builder.impl);
 
       reg = node->set->reg;
    } else {
@@ -482,11 +490,7 @@ rewrite_ssa_def(nir_ssa_def *def, void *void_state)
       if (def->parent_instr->type == nir_instr_type_load_const)
          return true;
 
-      reg = nir_local_reg_create(state->impl);
-      reg->name = def->name;
-      reg->num_components = def->num_components;
-      reg->bit_size = def->bit_size;
-      reg->num_array_elems = 0;
+      reg = create_reg_for_ssa_def(def, state->builder.impl);
    }
 
    nir_ssa_def_rewrite_uses(def, nir_src_for_reg(reg));
@@ -520,7 +524,7 @@ rewrite_ssa_def(nir_ssa_def *def, void *void_state)
 static bool
 resolve_registers_block(nir_block *block, struct from_ssa_state *state)
 {
-   nir_foreach_instr_safe(block, instr) {
+   nir_foreach_instr_safe(instr, block) {
       state->instr = instr;
       nir_foreach_ssa_def(instr, rewrite_ssa_def, state);
 
@@ -535,8 +539,7 @@ resolve_registers_block(nir_block *block, struct from_ssa_state *state)
 }
 
 static void
-emit_copy(nir_parallel_copy_instr *pcopy, nir_src src, nir_src dest_src,
-          void *mem_ctx)
+emit_copy(nir_builder *b, nir_src src, nir_src dest_src)
 {
    assert(!dest_src.is_ssa &&
           dest_src.reg.indirect == NULL &&
@@ -547,15 +550,15 @@ emit_copy(nir_parallel_copy_instr *pcopy, nir_src src, nir_src dest_src,
    else
       assert(src.reg.reg->num_components >= dest_src.reg.reg->num_components);
 
-   nir_alu_instr *mov = nir_alu_instr_create(mem_ctx, nir_op_imov);
+   nir_alu_instr *mov = nir_alu_instr_create(b->shader, nir_op_imov);
    nir_src_copy(&mov->src[0].src, &src, mov);
    mov->dest.dest = nir_dest_for_reg(dest_src.reg.reg);
    mov->dest.write_mask = (1 << dest_src.reg.reg->num_components) - 1;
 
-   nir_instr_insert_before(&pcopy->instr, &mov->instr);
+   nir_builder_instr_insert(b, &mov->instr);
 }
 
-/* Resolves a single parallel copy operation into a sequence of mov's
+/* Resolves a single parallel copy operation into a sequence of movs
  *
  * This is based on Algorithm 1 from "Revisiting Out-of-SSA Translation for
  * Correctness, Code Quality, and Efficiency" by Boissinot et. al..
@@ -582,7 +585,7 @@ resolve_parallel_copy(nir_parallel_copy_instr *pcopy,
                       struct from_ssa_state *state)
 {
    unsigned num_copies = 0;
-   nir_foreach_parallel_copy_entry(pcopy, entry) {
+   nir_foreach_parallel_copy_entry(entry, pcopy) {
       /* Sources may be SSA */
       if (!entry->src.is_ssa && entry->src.reg.reg == entry->dest.reg.reg)
          continue;
@@ -609,13 +612,15 @@ resolve_parallel_copy(nir_parallel_copy_instr *pcopy,
    NIR_VLA(int, to_do, num_copies * 2);
    int to_do_idx = -1;
 
+   state->builder.cursor = nir_before_instr(&pcopy->instr);
+
    /* Now we set everything up:
     *  - All values get assigned a temporary index
     *  - Current locations are set from sources
     *  - Predicessors are recorded from sources and destinations
     */
    int num_vals = 0;
-   nir_foreach_parallel_copy_entry(pcopy, entry) {
+   nir_foreach_parallel_copy_entry(entry, pcopy) {
       /* Sources may be SSA */
       if (!entry->src.is_ssa && entry->src.reg.reg == entry->dest.reg.reg)
          continue;
@@ -672,7 +677,7 @@ resolve_parallel_copy(nir_parallel_copy_instr *pcopy,
       while (ready_idx >= 0) {
          int b = ready[ready_idx--];
          int a = pred[b];
-         emit_copy(pcopy, values[loc[a]], values[b], state->mem_ctx);
+         emit_copy(&state->builder, values[loc[a]], values[b]);
 
          /* If any other copies want a they can find it at b */
          loc[a] = b;
@@ -698,7 +703,7 @@ resolve_parallel_copy(nir_parallel_copy_instr *pcopy,
        * backend can coalesce the (possibly multiple) temporaries.
        */
       assert(num_vals < num_copies * 2);
-      nir_register *reg = nir_local_reg_create(state->impl);
+      nir_register *reg = nir_local_reg_create(state->builder.impl);
       reg->name = "copy_temp";
       reg->num_array_elems = 0;
       if (values[b].is_ssa)
@@ -708,7 +713,7 @@ resolve_parallel_copy(nir_parallel_copy_instr *pcopy,
       values[num_vals].is_ssa = false;
       values[num_vals].reg.reg = reg;
 
-      emit_copy(pcopy, values[b], values[num_vals], state->mem_ctx);
+      emit_copy(&state->builder, values[b], values[num_vals]);
       loc[b] = num_vals;
       ready[++ready_idx] = b;
       num_vals++;
@@ -756,9 +761,8 @@ nir_convert_from_ssa_impl(nir_function_impl *impl, bool phi_webs_only)
 {
    struct from_ssa_state state;
 
-   state.mem_ctx = ralloc_parent(impl);
+   nir_builder_init(&state.builder, impl);
    state.dead_ctx = ralloc_context(NULL);
-   state.impl = impl;
    state.phi_webs_only = phi_webs_only;
    state.merge_node_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
                                                     _mesa_key_pointer_equal);
@@ -805,8 +809,168 @@ nir_convert_from_ssa_impl(nir_function_impl *impl, bool phi_webs_only)
 void
 nir_convert_from_ssa(nir_shader *shader, bool phi_webs_only)
 {
-   nir_foreach_function(shader, function) {
+   nir_foreach_function(function, shader) {
       if (function->impl)
          nir_convert_from_ssa_impl(function->impl, phi_webs_only);
    }
 }
+
+
+static void
+place_phi_read(nir_shader *shader, nir_register *reg,
+               nir_ssa_def *def, nir_block *block)
+{
+   if (block != def->parent_instr->block) {
+      /* Try to go up the single-successor tree */
+      bool all_single_successors = true;
+      struct set_entry *entry;
+      set_foreach(block->predecessors, entry) {
+         nir_block *pred = (nir_block *)entry->key;
+         if (pred->successors[0] && pred->successors[1]) {
+            all_single_successors = false;
+            break;
+         }
+      }
+
+      if (all_single_successors) {
+         /* All predecessors of this block have exactly one successor and it
+          * is this block so they must eventually lead here without
+          * intersecting each other.  Place the reads in the predecessors
+          * instead of this block.
+          */
+         set_foreach(block->predecessors, entry)
+            place_phi_read(shader, reg, def, (nir_block *)entry->key);
+         return;
+      }
+   }
+
+   nir_alu_instr *mov = nir_alu_instr_create(shader, nir_op_imov);
+   mov->src[0].src = nir_src_for_ssa(def);
+   mov->dest.dest = nir_dest_for_reg(reg);
+   mov->dest.write_mask = (1 << reg->num_components) - 1;
+   nir_instr_insert(nir_after_block_before_jump(block), &mov->instr);
+}
+
+/** Lower all of the phi nodes in a block to imovs to and from a register
+ *
+ * This provides a very quick-and-dirty out-of-SSA pass that you can run on a
+ * single block to convert all of its phis to a register and some imovs.
+ * The code that is generated, while not optimal for actual codegen in a
+ * back-end, is easy to generate, correct, and will turn into the same set of
+ * phis after you call regs_to_ssa and do some copy propagation.
+ *
+ * The one intelligent thing this pass does is that it places the moves from
+ * the phi sources as high up the predecessor tree as possible instead of in
+ * the exact predecessor.  This means that, in particular, it will crawl into
+ * the deepest nesting of any if-ladders.  In order to ensure that doing so is
+ * safe, it stops as soon as one of the predecessors has multiple successors.
+ */
+bool
+nir_lower_phis_to_regs_block(nir_block *block)
+{
+   nir_function_impl *impl = nir_cf_node_get_function(&block->cf_node);
+   nir_shader *shader = impl->function->shader;
+
+   bool progress = false;
+   nir_foreach_instr_safe(instr, block) {
+      if (instr->type != nir_instr_type_phi)
+         break;
+
+      nir_phi_instr *phi = nir_instr_as_phi(instr);
+      assert(phi->dest.is_ssa);
+
+      nir_register *reg = create_reg_for_ssa_def(&phi->dest.ssa, impl);
+
+      nir_alu_instr *mov = nir_alu_instr_create(shader, nir_op_imov);
+      mov->src[0].src = nir_src_for_reg(reg);
+      mov->dest.write_mask = (1 << phi->dest.ssa.num_components) - 1;
+      nir_ssa_dest_init(&mov->instr, &mov->dest.dest,
+                        phi->dest.ssa.num_components, phi->dest.ssa.bit_size,
+                        phi->dest.ssa.name);
+      nir_instr_insert(nir_after_instr(&phi->instr), &mov->instr);
+
+      nir_ssa_def_rewrite_uses(&phi->dest.ssa,
+                               nir_src_for_ssa(&mov->dest.dest.ssa));
+
+      nir_foreach_phi_src(src, phi) {
+         assert(src->src.is_ssa);
+         place_phi_read(shader, reg, src->src.ssa, src->pred);
+      }
+
+      nir_instr_remove(&phi->instr);
+
+      progress = true;
+   }
+
+   return progress;
+}
+
+struct ssa_def_to_reg_state {
+   nir_function_impl *impl;
+   bool progress;
+};
+
+static bool
+dest_replace_ssa_with_reg(nir_dest *dest, void *void_state)
+{
+   struct ssa_def_to_reg_state *state = void_state;
+
+   if (!dest->is_ssa)
+      return true;
+
+   nir_register *reg = create_reg_for_ssa_def(&dest->ssa, state->impl);
+
+   nir_ssa_def_rewrite_uses(&dest->ssa, nir_src_for_reg(reg));
+
+   nir_instr *instr = dest->ssa.parent_instr;
+   *dest = nir_dest_for_reg(reg);
+   dest->reg.parent_instr = instr;
+   list_addtail(&dest->reg.def_link, &reg->defs);
+
+   state->progress = true;
+
+   return true;
+}
+
+/** Lower all of the SSA defs in a block to registers
+ *
+ * This performs the very simple operation of blindly replacing all of the SSA
+ * defs in the given block with registers.  If not used carefully, this may
+ * result in phi nodes with register sources which is technically invalid.
+ * Fortunately, the register-based into-SSA pass handles them anyway.
+ */
+bool
+nir_lower_ssa_defs_to_regs_block(nir_block *block)
+{
+   nir_function_impl *impl = nir_cf_node_get_function(&block->cf_node);
+   nir_shader *shader = impl->function->shader;
+
+   struct ssa_def_to_reg_state state = {
+      .impl = impl,
+      .progress = false,
+   };
+
+   nir_foreach_instr(instr, block) {
+      if (instr->type == nir_instr_type_ssa_undef) {
+         /* Undefs are just a read of something never written. */
+         nir_ssa_undef_instr *undef = nir_instr_as_ssa_undef(instr);
+         nir_register *reg = create_reg_for_ssa_def(&undef->def, state.impl);
+         nir_ssa_def_rewrite_uses(&undef->def, nir_src_for_reg(reg));
+      } else if (instr->type == nir_instr_type_load_const) {
+         /* Constant loads are SSA-only, we need to insert a move */
+         nir_load_const_instr *load = nir_instr_as_load_const(instr);
+         nir_register *reg = create_reg_for_ssa_def(&load->def, state.impl);
+         nir_ssa_def_rewrite_uses(&load->def, nir_src_for_reg(reg));
+
+         nir_alu_instr *mov = nir_alu_instr_create(shader, nir_op_imov);
+         mov->src[0].src = nir_src_for_ssa(&load->def);
+         mov->dest.dest = nir_dest_for_reg(reg);
+         mov->dest.write_mask = (1 << reg->num_components) - 1;
+         nir_instr_insert(nir_after_instr(&load->instr), &mov->instr);
+      } else {
+         nir_foreach_dest(instr, dest_replace_ssa_with_reg, &state);
+      }
+   }
+
+   return state.progress;
+}