nir,spirv: Rework function calls
[mesa.git] / src / compiler / glsl / opt_copy_propagation.cpp
index 02628cd7e52858f78dd989d95d1c52dad08e33b2..206dffe4f1c033eb427e1ce25b116aa157eb152b 100644 (file)
 #include "ir_optimization.h"
 #include "compiler/glsl_types.h"
 #include "util/hash_table.h"
+#include "util/set.h"
 
 namespace {
 
-class kill_entry : public exec_node
-{
-public:
-   kill_entry(ir_variable *var)
-   {
-      assert(var);
-      this->var = var;
-   }
-
-   ir_variable *var;
-};
-
 class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
 public:
    ir_copy_propagation_visitor()
    {
       progress = false;
       mem_ctx = ralloc_context(0);
+      lin_ctx = linear_alloc_parent(mem_ctx, 0);
       acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,
                                     _mesa_key_pointer_equal);
-      this->kills = new(mem_ctx) exec_list;
+      kills = _mesa_set_create(mem_ctx, _mesa_hash_pointer,
+                               _mesa_key_pointer_equal);
       killed_all = false;
    }
    ~ir_copy_propagation_visitor()
@@ -84,17 +75,18 @@ public:
 
    /** Hash of lhs->rhs: The available copies to propagate */
    hash_table *acp;
+
    /**
-    * List of kill_entry: The variables whose values were killed in this
-    * block.
+    * Set of ir_variables: Whose values were killed in this block.
     */
-   exec_list *kills;
+   set *kills;
 
    bool progress;
 
    bool killed_all;
 
    void *mem_ctx;
+   void *lin_ctx;
 };
 
 } /* unnamed namespace */
@@ -107,18 +99,19 @@ ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
     * main() at link time, so they're irrelevant to us.
     */
    hash_table *orig_acp = this->acp;
-   exec_list *orig_kills = this->kills;
+   set *orig_kills = this->kills;
    bool orig_killed_all = this->killed_all;
 
    acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
                                  _mesa_key_pointer_equal);
-   this->kills = new(mem_ctx) exec_list;
+   kills = _mesa_set_create(NULL, _mesa_hash_pointer,
+                            _mesa_key_pointer_equal);
    this->killed_all = false;
 
    visit_list_elements(this, &ir->body);
 
    _mesa_hash_table_destroy(acp, NULL);
-   ralloc_free(this->kills);
+   _mesa_set_destroy(kills, NULL);
 
    this->kills = orig_kills;
    this->acp = orig_acp;
@@ -181,11 +174,34 @@ ir_copy_propagation_visitor::visit_enter(ir_call *ir)
       }
    }
 
-   /* Since we're unlinked, we don't (necessarily) know the side effects of
-    * this call.  So kill all copies.
+   /* Since this pass can run when unlinked, we don't (necessarily) know
+    * the side effects of calls.  (When linked, most calls are inlined
+    * anyway, so it doesn't matter much.)
+    *
+    * One place where this does matter is IR intrinsics.  They're never
+    * inlined.  We also know what they do - while some have side effects
+    * (such as image writes), none edit random global variables.  So we
+    * can assume they're side-effect free (other than the return value
+    * and out parameters).
     */
-   _mesa_hash_table_clear(acp, NULL);
-   this->killed_all = true;
+   if (!ir->callee->is_intrinsic()) {
+      _mesa_hash_table_clear(acp, NULL);
+      this->killed_all = true;
+   } else {
+      if (ir->return_deref)
+         kill(ir->return_deref->var);
+
+      foreach_two_lists(formal_node, &ir->callee->parameters,
+                        actual_node, &ir->actual_parameters) {
+         ir_variable *sig_param = (ir_variable *) formal_node;
+         if (sig_param->data.mode == ir_var_function_out ||
+             sig_param->data.mode == ir_var_function_inout) {
+            ir_rvalue *ir = (ir_rvalue *) actual_node;
+            ir_variable *var = ir->variable_referenced();
+            kill(var);
+         }
+      }
+   }
 
    return visit_continue_with_parent;
 }
@@ -194,19 +210,15 @@ void
 ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
 {
    hash_table *orig_acp = this->acp;
-   exec_list *orig_kills = this->kills;
+   set *orig_kills = this->kills;
    bool orig_killed_all = this->killed_all;
 
-   acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
-                                 _mesa_key_pointer_equal);
-   this->kills = new(mem_ctx) exec_list;
+   kills = _mesa_set_create(NULL, _mesa_hash_pointer,
+                            _mesa_key_pointer_equal);
    this->killed_all = false;
 
    /* Populate the initial acp with a copy of the original */
-   struct hash_entry *entry;
-   hash_table_foreach(orig_acp, entry) {
-      _mesa_hash_table_insert(acp, entry->key, entry->data);
-   }
+   acp = _mesa_hash_table_clone(orig_acp, NULL);
 
    visit_list_elements(this, instructions);
 
@@ -214,17 +226,18 @@ ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
       _mesa_hash_table_clear(orig_acp, NULL);
    }
 
-   exec_list *new_kills = this->kills;
+   set *new_kills = this->kills;
    this->kills = orig_kills;
    _mesa_hash_table_destroy(acp, NULL);
    this->acp = orig_acp;
    this->killed_all = this->killed_all || orig_killed_all;
 
-   foreach_in_list(kill_entry, k, new_kills) {
-      kill(k->var);
+   struct set_entry *s_entry;
+   set_foreach(new_kills, s_entry) {
+      kill((ir_variable *) s_entry->key);
    }
 
-   ralloc_free(new_kills);
+   _mesa_set_destroy(new_kills, NULL);
 }
 
 ir_visitor_status
@@ -243,19 +256,18 @@ void
 ir_copy_propagation_visitor::handle_loop(ir_loop *ir, bool keep_acp)
 {
    hash_table *orig_acp = this->acp;
-   exec_list *orig_kills = this->kills;
+   set *orig_kills = this->kills;
    bool orig_killed_all = this->killed_all;
 
-   acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
-                                 _mesa_key_pointer_equal);
-   this->kills = new(mem_ctx) exec_list;
+   kills = _mesa_set_create(NULL, _mesa_hash_pointer,
+                            _mesa_key_pointer_equal);
    this->killed_all = false;
 
    if (keep_acp) {
-      struct hash_entry *entry;
-      hash_table_foreach(orig_acp, entry) {
-         _mesa_hash_table_insert(acp, entry->key, entry->data);
-      }
+      acp = _mesa_hash_table_clone(orig_acp, NULL);
+   } else {
+      acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
+                                    _mesa_key_pointer_equal);
    }
 
    visit_list_elements(this, &ir->body_instructions);
@@ -264,17 +276,18 @@ ir_copy_propagation_visitor::handle_loop(ir_loop *ir, bool keep_acp)
       _mesa_hash_table_clear(orig_acp, NULL);
    }
 
-   exec_list *new_kills = this->kills;
+   set *new_kills = this->kills;
    this->kills = orig_kills;
    _mesa_hash_table_destroy(acp, NULL);
    this->acp = orig_acp;
    this->killed_all = this->killed_all || orig_killed_all;
 
-   foreach_in_list(kill_entry, k, new_kills) {
-      kill(k->var);
+   struct set_entry *entry;
+   set_foreach(new_kills, entry) {
+      kill((ir_variable *) entry->key);
    }
 
-   ralloc_free(new_kills);
+   _mesa_set_destroy(new_kills, NULL);
 }
 
 ir_visitor_status
@@ -311,9 +324,8 @@ ir_copy_propagation_visitor::kill(ir_variable *var)
       }
    }
 
-   /* Add the LHS variable to the list of killed variables in this block.
-    */
-   this->kills->push_tail(new(this->kills) kill_entry(var));
+   /* Add the LHS variable to the set of killed variables in this block. */
+   _mesa_set_add(kills, var);
 }
 
 /**
@@ -329,20 +341,15 @@ ir_copy_propagation_visitor::add_copy(ir_assignment *ir)
    ir_variable *lhs_var = ir->whole_variable_written();
    ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
 
-   if ((lhs_var != NULL) && (rhs_var != NULL)) {
-      if (lhs_var == rhs_var) {
-        /* This is a dumb assignment, but we've conveniently noticed
-         * it here.  Removing it now would mess up the loop iteration
-         * calling us.  Just flag it to not execute, and someone else
-         * will clean up the mess.
-         */
-        ir->condition = new(ralloc_parent(ir)) ir_constant(false);
-        this->progress = true;
-      } else if (lhs_var->data.mode != ir_var_shader_storage &&
-                 lhs_var->data.mode != ir_var_shader_shared &&
-                 lhs_var->data.precise == rhs_var->data.precise) {
-         assert(lhs_var);
-         assert(rhs_var);
+   /* Don't try to remove a dumb assignment of a variable to itself.  Removing
+    * it now would mess up the loop iteration calling us.
+    */
+   if (lhs_var != NULL && rhs_var != NULL && lhs_var != rhs_var) {
+      if (lhs_var->data.mode != ir_var_shader_storage &&
+          lhs_var->data.mode != ir_var_shader_shared &&
+          rhs_var->data.mode != ir_var_shader_storage &&
+          rhs_var->data.mode != ir_var_shader_shared &&
+          lhs_var->data.precise == rhs_var->data.precise) {
          _mesa_hash_table_insert(acp, lhs_var, rhs_var);
       }
    }