nir: move to compiler/
[mesa.git] / src / glsl / opt_copy_propagation_elements.cpp
index 8541d9a8ee1ca80904b1e261047242211a844f84..a6791801943fab0b39b44df4ad02e8f1eeb74065 100644 (file)
 #include "ir_rvalue_visitor.h"
 #include "ir_basic_block.h"
 #include "ir_optimization.h"
-#include "glsl_types.h"
+#include "compiler/glsl_types.h"
 
 static bool debug = false;
 
+namespace {
+
 class acp_entry : public exec_node
 {
 public:
@@ -93,6 +95,7 @@ public:
    ir_copy_propagation_elements_visitor()
    {
       this->progress = false;
+      this->killed_all = false;
       this->mem_ctx = ralloc_context(NULL);
       this->shader_mem_ctx = NULL;
       this->acp = new(mem_ctx) exec_list;
@@ -108,6 +111,7 @@ public:
    virtual ir_visitor_status visit_leave(class ir_assignment *);
    virtual ir_visitor_status visit_enter(class ir_call *);
    virtual ir_visitor_status visit_enter(class ir_if *);
+   virtual ir_visitor_status visit_leave(class ir_swizzle *);
 
    void handle_rvalue(ir_rvalue **rvalue);
 
@@ -133,6 +137,8 @@ public:
    void *shader_mem_ctx;
 };
 
+} /* unnamed namespace */
+
 ir_visitor_status
 ir_copy_propagation_elements_visitor::visit_enter(ir_function_signature *ir)
 {
@@ -150,6 +156,9 @@ ir_copy_propagation_elements_visitor::visit_enter(ir_function_signature *ir)
 
    visit_list_elements(this, &ir->body);
 
+   ralloc_free(this->acp);
+   ralloc_free(this->kills);
+
    this->kills = orig_kills;
    this->acp = orig_acp;
    this->killed_all = orig_killed_all;
@@ -161,9 +170,16 @@ ir_visitor_status
 ir_copy_propagation_elements_visitor::visit_leave(ir_assignment *ir)
 {
    ir_dereference_variable *lhs = ir->lhs->as_dereference_variable();
+   ir_variable *var = ir->lhs->variable_referenced();
+
+   if (var->type->is_scalar() || var->type->is_vector()) {
+      kill_entry *k;
+
+      if (lhs)
+        k = new(this->kills) kill_entry(var, ir->write_mask);
+      else
+        k = new(this->kills) kill_entry(var, ~0);
 
-   if (lhs && (lhs->type->is_scalar() || lhs->type->is_vector())) {
-      kill_entry *k = new(mem_ctx) kill_entry(lhs->var, ir->write_mask);
       kill(k);
    }
 
@@ -172,6 +188,15 @@ ir_copy_propagation_elements_visitor::visit_leave(ir_assignment *ir)
    return visit_continue;
 }
 
+ir_visitor_status
+ir_copy_propagation_elements_visitor::visit_leave(ir_swizzle *)
+{
+   /* Don't visit the values of swizzles since they are handled while
+    * visiting the swizzle itself.
+    */
+   return visit_continue;
+}
+
 /**
  * Replaces dereferences of ACP RHS variables with ACP LHS variables.
  *
@@ -185,8 +210,9 @@ ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir)
    int swizzle_chan[4];
    ir_dereference_variable *deref_var;
    ir_variable *source[4] = {NULL, NULL, NULL, NULL};
-   int source_chan[4];
+   int source_chan[4] = {0, 0, 0, 0};
    int chans;
+   bool noop_swizzle = true;
 
    if (!*ir)
       return;
@@ -222,14 +248,15 @@ ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir)
    /* Try to find ACP entries covering swizzle_chan[], hoping they're
     * the same source variable.
     */
-   foreach_iter(exec_list_iterator, iter, *this->acp) {
-      acp_entry *entry = (acp_entry *)iter.get();
-
+   foreach_in_list(acp_entry, entry, this->acp) {
       if (var == entry->lhs) {
         for (int c = 0; c < chans; c++) {
            if (entry->write_mask & (1 << swizzle_chan[c])) {
               source[c] = entry->rhs;
               source_chan[c] = entry->swizzle[swizzle_chan[c]];
+
+               if (source_chan[c] != swizzle_chan[c])
+                  noop_swizzle = false;
            }
         }
       }
@@ -246,6 +273,12 @@ ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir)
    if (!shader_mem_ctx)
       shader_mem_ctx = ralloc_parent(deref_var);
 
+   /* Don't pointlessly replace the rvalue with itself (or a noop swizzle
+    * of itself, which would just be deleted by opt_noop_swizzle).
+    */
+   if (source[0] == var && noop_swizzle)
+      return;
+
    if (debug) {
       printf("Copy propagation from:\n");
       (*ir)->print();
@@ -258,6 +291,7 @@ ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir)
                                        source_chan[2],
                                        source_chan[3],
                                        chans);
+   progress = true;
 
    if (debug) {
       printf("to:\n");
@@ -271,14 +305,14 @@ ir_visitor_status
 ir_copy_propagation_elements_visitor::visit_enter(ir_call *ir)
 {
    /* Do copy propagation on call parameters, but skip any out params */
-   exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
-   foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
-      ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
-      ir_instruction *ir = (ir_instruction *)iter.get();
-      if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
+   foreach_two_lists(formal_node, &ir->callee->parameters,
+                     actual_node, &ir->actual_parameters) {
+      ir_variable *sig_param = (ir_variable *) formal_node;
+      ir_rvalue *ir = (ir_rvalue *) actual_node;
+      if (sig_param->data.mode != ir_var_function_out
+          && sig_param->data.mode != ir_var_function_inout) {
          ir->accept(this);
       }
-      sig_param_iter.next();
    }
 
    /* Since we're unlinked, we don't (necessarily) know the side effects of
@@ -302,9 +336,8 @@ ir_copy_propagation_elements_visitor::handle_if_block(exec_list *instructions)
    this->killed_all = false;
 
    /* Populate the initial acp with a copy of the original */
-   foreach_iter(exec_list_iterator, iter, *orig_acp) {
-      acp_entry *a = (acp_entry *)iter.get();
-      this->acp->push_tail(new(this->mem_ctx) acp_entry(a));
+   foreach_in_list(acp_entry, a, orig_acp) {
+      this->acp->push_tail(new(this->acp) acp_entry(a));
    }
 
    visit_list_elements(this, instructions);
@@ -315,16 +348,18 @@ ir_copy_propagation_elements_visitor::handle_if_block(exec_list *instructions)
 
    exec_list *new_kills = this->kills;
    this->kills = orig_kills;
+   ralloc_free(this->acp);
    this->acp = orig_acp;
    this->killed_all = this->killed_all || orig_killed_all;
 
    /* Move the new kills into the parent block's list, removing them
     * from the parent's ACP list in the process.
     */
-   foreach_list_safe(node, new_kills) {
-      kill_entry *k = (kill_entry *)node;
+   foreach_in_list_safe(kill_entry, k, new_kills) {
       kill(k);
    }
+
+   ralloc_free(new_kills);
 }
 
 ir_visitor_status
@@ -362,14 +397,16 @@ ir_copy_propagation_elements_visitor::visit_enter(ir_loop *ir)
 
    exec_list *new_kills = this->kills;
    this->kills = orig_kills;
+   ralloc_free(this->acp);
    this->acp = orig_acp;
    this->killed_all = this->killed_all || orig_killed_all;
 
-   foreach_list_safe(node, new_kills) {
-      kill_entry *k = (kill_entry *)node;
+   foreach_in_list_safe(kill_entry, k, new_kills) {
       kill(k);
    }
 
+   ralloc_free(new_kills);
+
    /* already descended into the children. */
    return visit_continue_with_parent;
 }
@@ -378,13 +415,13 @@ ir_copy_propagation_elements_visitor::visit_enter(ir_loop *ir)
 void
 ir_copy_propagation_elements_visitor::kill(kill_entry *k)
 {
-   foreach_list_safe(node, acp) {
-      acp_entry *entry = (acp_entry *)node;
-
+   foreach_in_list_safe(acp_entry, entry, acp) {
       if (entry->lhs == k->var) {
         entry->write_mask = entry->write_mask & ~k->write_mask;
-        if (entry->write_mask == 0)
+        if (entry->write_mask == 0) {
            entry->remove();
+           continue;
+        }
       }
       if (entry->rhs == k->var) {
         entry->remove();
@@ -395,6 +432,7 @@ ir_copy_propagation_elements_visitor::kill(kill_entry *k)
    if (k->next)
       k->remove();
 
+   ralloc_steal(this->kills, k);
    this->kills->push_tail(k);
 }
 
@@ -442,7 +480,20 @@ ir_copy_propagation_elements_visitor::add_copy(ir_assignment *ir)
         swizzle[i] = orig_swizzle[j++];
    }
 
-   entry = new(this->mem_ctx) acp_entry(lhs->var, rhs->var, ir->write_mask,
+   int write_mask = ir->write_mask;
+   if (lhs->var == rhs->var) {
+      /* If this is a copy from the variable to itself, then we need
+       * to be sure not to include the updated channels from this
+       * instruction in the set of new source channels to be
+       * copy-propagated from.
+       */
+      for (int i = 0; i < 4; i++) {
+        if (ir->write_mask & (1 << orig_swizzle[i]))
+           write_mask &= ~(1 << i);
+      }
+   }
+
+   entry = new(this->mem_ctx) acp_entry(lhs->var, rhs->var, write_mask,
                                        swizzle);
    this->acp->push_tail(entry);
 }