glsl2: Add a pass for removing unused functions.
authorEric Anholt <eric@anholt.net>
Thu, 5 Aug 2010 17:09:12 +0000 (10:09 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 5 Aug 2010 17:18:31 +0000 (10:18 -0700)
For a shader involving many small functions, this avoids running
optimization across all of them after they've been inlined
post-linking.

Reduces the runtime of linking and running a fragment shader from Yo
Frankie from 1.6 seconds to 0.9 seconds (-44.9%, +/- 3.3%).

src/glsl/Makefile
src/glsl/glsl_symbol_table.h
src/glsl/ir.h
src/glsl/ir_dead_functions.cpp [new file with mode: 0644]
src/glsl/ir_optimization.h
src/glsl/linker.cpp

index 3102947494cc4379d55b69e050070f4a184e2a4b..844385792aa9f5c05f82a8555159de9757a6057f 100644 (file)
@@ -39,6 +39,7 @@ CXX_SOURCES = \
        ir.cpp \
        ir_dead_code.cpp \
        ir_dead_code_local.cpp \
+       ir_dead_functions.cpp \
        ir_div_to_mul_rcp.cpp \
        ir_expression_flattening.cpp \
        ir_function_can_inline.cpp \
index 27e825597c52a622222f8ee38825c72adcbd9bf9..02e4542cf3d0d3ca91b5e474154b4c01ff59a9fd 100644 (file)
@@ -133,6 +133,12 @@ public:
       return _mesa_symbol_table_add_symbol(table, glsl_function_name_space,
                                           name, f) == 0;
    }
+
+   bool remove_function(const char *name, ir_function *f)
+   {
+      return _mesa_symbol_table_add_symbol(table, glsl_function_name_space,
+                                          name, f) == 0;
+   }
    /*@}*/
 
    /**
index e61485813dce843610684be8a5dd3c664dd789d8..f58602515efce815bffb3893cf8f3e89ab9f5e05 100644 (file)
@@ -410,7 +410,6 @@ public:
     */
    const char *name;
 
-private:
    /**
     * List of ir_function_signature for each overloaded function with this name.
     */
diff --git a/src/glsl/ir_dead_functions.cpp b/src/glsl/ir_dead_functions.cpp
new file mode 100644 (file)
index 0000000..2655444
--- /dev/null
@@ -0,0 +1,151 @@
+ /*
+  * Copyright © 2010 Intel Corporation
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice (including the next
+  * paragraph) shall be included in all copies or substantial portions of the
+  * Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  * DEALINGS IN THE SOFTWARE.
+  */
+
+ /**
+  * \file ir_dead_functions.cpp
+  *
+  * Eliminates unused functions from the linked program.
+  */
+
+ #include "ir.h"
+ #include "ir_visitor.h"
+ #include "ir_expression_flattening.h"
+ #include "glsl_types.h"
+
+ class signature_entry : public exec_node
+ {
+ public:
+    signature_entry(ir_function_signature *sig)
+    {
+       this->signature = sig;
+       this->used = false;
+    }
+
+    ir_function_signature *signature;
+    bool used;
+ };
+
+ class ir_dead_functions_visitor : public ir_hierarchical_visitor {
+ public:
+    ir_dead_functions_visitor()
+    {
+       this->mem_ctx = talloc_new(NULL);
+    }
+
+    ~ir_dead_functions_visitor()
+    {
+       talloc_free(this->mem_ctx);
+    }
+
+    virtual ir_visitor_status visit_enter(ir_function_signature *);
+    virtual ir_visitor_status visit_enter(ir_call *);
+
+    signature_entry *get_signature_entry(ir_function_signature *var);
+
+    bool (*predicate)(ir_instruction *ir);
+
+    /* List of signature_entry */
+    exec_list signature_list;
+    void *mem_ctx;
+ };
+
+
+ signature_entry *
+ ir_dead_functions_visitor::get_signature_entry(ir_function_signature *sig)
+ {
+    foreach_iter(exec_list_iterator, iter, this->signature_list) {
+       signature_entry *entry = (signature_entry *)iter.get();
+       if (entry->signature == sig)
+         return entry;
+    }
+
+    signature_entry *entry = new(mem_ctx) signature_entry(sig);
+    this->signature_list.push_tail(entry);
+    return entry;
+ }
+
+
+ ir_visitor_status
+ ir_dead_functions_visitor::visit_enter(ir_function_signature *ir)
+ {
+    signature_entry *entry = this->get_signature_entry(ir);
+
+    if (strcmp(ir->function_name(), "main") == 0) {
+       entry->used = true;
+    }
+
+    return visit_continue;
+ }
+
+
+ ir_visitor_status
+ ir_dead_functions_visitor::visit_enter(ir_call *ir)
+ {
+    signature_entry *entry = this->get_signature_entry(ir->get_callee());
+
+    entry->used = true;
+
+   return visit_continue;
+}
+
+bool
+do_dead_functions(exec_list *instructions)
+{
+   ir_dead_functions_visitor v;
+   bool progress = false;
+
+   visit_list_elements(&v, instructions);
+
+   /* Now that we've figured out which function signatures are used, remove
+    * the unused ones, and remove function definitions that have no more
+    * signatures.
+    */
+    foreach_iter(exec_list_iterator, iter, v.signature_list) {
+      signature_entry *entry = (signature_entry *)iter.get();
+
+      if (!entry->used) {
+        entry->signature->remove();
+        progress = true;
+      }
+      delete(entry);
+   }
+
+   /* We don't just do this above when we nuked a signature because of
+    * const pointers.
+    */
+   foreach_iter(exec_list_iterator, iter, *instructions) {
+      ir_instruction *ir = (ir_instruction *)iter.get();
+      ir_function *func = ir->as_function();
+
+      if (func && func->signatures.is_empty()) {
+        /* At this point (post-linking), the symbol table is no
+         * longer in use, so not removing the function from the
+         * symbol table should be OK.
+         */
+        func->remove();
+        progress = true;
+      }
+   }
+
+   return progress;
+}
index 55ec327193655fd575de7cea6916e744f0790737..e0c0715cf5b4d96197564dd372b7bdbee21c9d2d 100644 (file)
@@ -36,6 +36,7 @@ bool do_copy_propagation(exec_list *instructions);
 bool do_dead_code(exec_list *instructions);
 bool do_dead_code_local(exec_list *instructions);
 bool do_dead_code_unlinked(exec_list *instructions);
+bool do_dead_functions(exec_list *instructions);
 bool do_div_to_mul_rcp(exec_list *instructions);
 bool do_function_inlining(exec_list *instructions);
 bool do_if_return(exec_list *instructions);
index 94db57d6a52994e7d64e30192a4f04e5e0e9891c..f9e24ca0f113f259fb1df7a6d20dac4d4117b4e0 100644 (file)
@@ -1286,6 +1286,7 @@ link_shaders(struct gl_shader_program *prog)
         progress = false;
 
         progress = do_function_inlining(ir) || progress;
+        progress = do_dead_functions(ir) || progress;
         progress = do_if_simplification(ir) || progress;
         progress = do_copy_propagation(ir) || progress;
         progress = do_dead_code_local(ir) || progress;