Add a virtual clone() method to ir_instruction.
[mesa.git] / ir_copy_propagation.cpp
index 8eb1ebde38c5e6aca0daf873bdcd43ea4fb48806..16a2ba79bf6f562774663ce8fe21bee0cdb4bf51 100644 (file)
  */
 
 /**
- * \file ir_dead_code.cpp
+ * \file ir_copy_propagation.cpp
  *
- * Eliminates dead assignments and variable declarations from the code.
+ * Moves usage of recently-copied variables to the previous copy of
+ * the variable within basic blocks.
+ *
+ * This should reduce the number of MOV instructions in the generated
+ * programs unless copy propagation is also done on the LIR, and may
+ * help anyway by triggering other optimizations that live in the HIR.
  */
 
-#include <stdio.h>
 #include "ir.h"
 #include "ir_visitor.h"
 #include "ir_basic_block.h"
-#include "ir_copy_propagation.h"
+#include "ir_optimization.h"
 #include "glsl_types.h"
 
 class acp_entry : public exec_node
@@ -49,90 +53,59 @@ public:
    ir_variable *rhs;
 };
 
-class ir_copy_propagation_visitor : public ir_visitor {
+class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
 public:
    ir_copy_propagation_visitor(exec_list *acp)
    {
       progress = false;
+      in_lhs = false;
       this->acp = acp;
    }
 
-   /**
-    * \name Visit methods
-    *
-    * As typical for the visitor pattern, there must be one \c visit method for
-    * each concrete subclass of \c ir_instruction.  Virtual base classes within
-    * the hierarchy should not have \c visit methods.
-    */
-   /*@{*/
-   virtual void visit(ir_variable *);
-   virtual void visit(ir_loop *);
-   virtual void visit(ir_loop_jump *);
-   virtual void visit(ir_function_signature *);
-   virtual void visit(ir_function *);
-   virtual void visit(ir_expression *);
-   virtual void visit(ir_swizzle *);
-   virtual void visit(ir_dereference *);
-   virtual void visit(ir_assignment *);
-   virtual void visit(ir_constant *);
-   virtual void visit(ir_call *);
-   virtual void visit(ir_return *);
-   virtual void visit(ir_if *);
-   /*@}*/
+   virtual ir_visitor_status visit(class ir_dereference_variable *);
+   virtual ir_visitor_status visit_enter(class ir_loop *);
+   virtual ir_visitor_status visit_enter(class ir_function_signature *);
+   virtual ir_visitor_status visit_enter(class ir_function *);
+   virtual ir_visitor_status visit_enter(class ir_assignment *);
+   virtual ir_visitor_status visit_enter(class ir_call *);
+   virtual ir_visitor_status visit_enter(class ir_if *);
 
    /** List of acp_entry */
    exec_list *acp;
    bool progress;
+
+   /** Currently in the LHS of an assignment? */
+   bool in_lhs;
 };
 
 
-void
-ir_copy_propagation_visitor::visit(ir_variable *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
 {
    (void)ir;
+   return visit_continue_with_parent;
 }
 
-
-void
-ir_copy_propagation_visitor::visit(ir_loop *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
 {
    (void)ir;
+   return visit_continue_with_parent;
 }
 
-void
-ir_copy_propagation_visitor::visit(ir_loop_jump *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_assignment *ir)
 {
    (void) ir;
+   this->in_lhs = true;
+   return visit_continue;
 }
 
-
-void
-ir_copy_propagation_visitor::visit(ir_function_signature *ir)
-{
-   (void)ir;
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_function *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function *ir)
 {
    (void) ir;
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_expression *ir)
-{
-   unsigned int operand;
-
-   for (operand = 0; operand < ir->get_num_operands(); operand++) {
-      ir->operands[operand]->accept(this);
-   }
-}
-
-
-void
-ir_copy_propagation_visitor::visit(ir_swizzle *ir)
-{
-   ir->val->accept(this);
+   return visit_continue_with_parent;
 }
 
 /**
@@ -142,107 +115,78 @@ ir_copy_propagation_visitor::visit(ir_swizzle *ir)
  * rewriting of ir_dereference means that the ir_dereference instance
  * must not be shared by multiple IR operations!
  */
-void
-ir_copy_propagation_visitor::visit(ir_dereference *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
 {
-   ir_variable *var;
-
-   if (ir->mode == ir_dereference::ir_reference_array) {
-      ir->selector.array_index->accept(this);
-   }
-
-   var = ir->var->as_variable();
-   if (var) {
-      foreach_iter(exec_list_iterator, iter, *this->acp) {
-        acp_entry *entry = (acp_entry *)iter.get();
-
-        if (var == entry->lhs) {
-           ir->var = entry->rhs;
-           this->progress = true;
-           break;
-        }
-      }
-   } else {
-      ir->var->accept(this);
-   }
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_assignment *ir)
-{
-   if (ir->condition)
-      ir->condition->accept(this);
-
    /* Ignores the LHS.  Don't want to rewrite the LHS to point at some
     * other storage!
     */
+   if (this->in_lhs) {
+      this->in_lhs = false;
+      return visit_continue;
+   }
 
-   ir->rhs->accept(this);
-}
+   ir_variable *var = ir->variable_referenced();
 
+   foreach_iter(exec_list_iterator, iter, *this->acp) {
+      acp_entry *entry = (acp_entry *)iter.get();
 
-void
-ir_copy_propagation_visitor::visit(ir_constant *ir)
-{
-   (void) ir;
+      if (var == entry->lhs) {
+        ir->var = entry->rhs;
+        this->progress = true;
+        break;
+      }
+   }
+
+   return visit_continue;
 }
 
 
-void
-ir_copy_propagation_visitor::visit(ir_call *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_call *ir)
 {
    (void)ir;
 
    /* Note, if we were to do copy propagation to parameters of calls, we'd
     * have to be careful about out params.
     */
+   return visit_continue_with_parent;
 }
 
 
-void
-ir_copy_propagation_visitor::visit(ir_return *ir)
-{
-   ir_rvalue *val = ir->get_value();
-
-   if (val)
-      val->accept(this);
-}
-
-
-void
-ir_copy_propagation_visitor::visit(ir_if *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_if *ir)
 {
    ir->condition->accept(this);
+
+   /* Do not traverse into the body of the if-statement since that is a
+    * different basic block.
+    */
+   return visit_continue_with_parent;
 }
 
-static void
+static bool
 propagate_copies(ir_instruction *ir, exec_list *acp)
 {
    ir_copy_propagation_visitor v(acp);
 
    ir->accept(&v);
+
+   return v.progress;
 }
 
 static void
 kill_invalidated_copies(ir_assignment *ir, exec_list *acp)
 {
-   ir_dereference *lhs_deref = ir->lhs->as_dereference();
-
-   /* Only handle simple dereferences for now. */
-   if (lhs_deref &&
-       lhs_deref->mode == ir_dereference::ir_reference_variable) {
-      ir_variable *var = lhs_deref->var->as_variable();
+   ir_variable *var = ir->lhs->variable_referenced();
+   assert(var != NULL);
 
-      foreach_iter(exec_list_iterator, iter, *acp) {
-        acp_entry *entry = (acp_entry *)iter.get();
+   foreach_iter(exec_list_iterator, iter, *acp) {
+      acp_entry *entry = (acp_entry *)iter.get();
 
-        if (entry->lhs == var || entry->rhs == var) {
-           entry->remove();
-        }
+      if (entry->lhs == var || entry->rhs == var) {
+        entry->remove();
       }
-   } else {
-      /* FINISHME: Only clear out the entries we overwrote here. */
-      acp->make_empty();
    }
 }
 
@@ -261,32 +205,30 @@ add_copy(ir_assignment *ir, exec_list *acp)
         return;
    }
 
-   ir_dereference *lhs_deref = ir->lhs->as_dereference();
-   if (!lhs_deref || lhs_deref->mode != ir_dereference::ir_reference_variable)
-      return;
-   ir_variable *lhs_var = lhs_deref->var->as_variable();
+   ir_variable *lhs_var = ir->lhs->whole_variable_referenced();
+   ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
 
-   ir_dereference *rhs_deref = ir->rhs->as_dereference();
-   if (!rhs_deref || rhs_deref->mode != ir_dereference::ir_reference_variable)
-      return;
-   ir_variable *rhs_var = rhs_deref->var->as_variable();
-
-   entry = new acp_entry(lhs_var, rhs_var);
-   acp->push_tail(entry);
+   if ((lhs_var != NULL) && (rhs_var != NULL)) {
+      entry = new acp_entry(lhs_var, rhs_var);
+      acp->push_tail(entry);
+   }
 }
 
 static void
 copy_propagation_basic_block(ir_instruction *first,
-                            ir_instruction *last)
+                            ir_instruction *last,
+                            void *data)
 {
    ir_instruction *ir;
    /* List of avaialble_copy */
    exec_list acp;
+   bool *out_progress = (bool *)data;
+   bool progress = false;
 
    for (ir = first;; ir = (ir_instruction *)ir->next) {
       ir_assignment *ir_assign = ir->as_assignment();
 
-      propagate_copies(ir, &acp);
+      progress = propagate_copies(ir, &acp) || progress;
 
       if (ir_assign) {
         kill_invalidated_copies(ir_assign, &acp);
@@ -296,6 +238,7 @@ copy_propagation_basic_block(ir_instruction *first,
       if (ir == last)
         break;
    }
+   *out_progress = progress;
 }
 
 /**
@@ -306,8 +249,7 @@ do_copy_propagation(exec_list *instructions)
 {
    bool progress = false;
 
-   call_for_basic_blocks(instructions, copy_propagation_basic_block);
+   call_for_basic_blocks(instructions, copy_propagation_basic_block, &progress);
 
-   /* FINISHME: Return a legit progress value. */
    return progress;
 }