Mesa (glsl2): glsl2: Add a pass for removing unused functions.

Eric Anholt anholt at kemper.freedesktop.org
Thu Aug 5 17:23:44 UTC 2010


Module: Mesa
Branch: glsl2
Commit: 2e853ca23c8670246dd4efcee0706f68097652f7
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=2e853ca23c8670246dd4efcee0706f68097652f7

Author: Eric Anholt <eric at anholt.net>
Date:   Thu Aug  5 10:09:12 2010 -0700

glsl2: Add a pass for removing unused functions.

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              |    1 +
 src/glsl/glsl_symbol_table.h   |    6 ++
 src/glsl/ir.h                  |    1 -
 src/glsl/ir_dead_functions.cpp |  151 ++++++++++++++++++++++++++++++++++++++++
 src/glsl/ir_optimization.h     |    1 +
 src/glsl/linker.cpp            |    1 +
 6 files changed, 160 insertions(+), 1 deletions(-)

diff --git a/src/glsl/Makefile b/src/glsl/Makefile
index 3102947..8443857 100644
--- a/src/glsl/Makefile
+++ b/src/glsl/Makefile
@@ -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 \
diff --git a/src/glsl/glsl_symbol_table.h b/src/glsl/glsl_symbol_table.h
index 27e8255..02e4542 100644
--- a/src/glsl/glsl_symbol_table.h
+++ b/src/glsl/glsl_symbol_table.h
@@ -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;
+   }
    /*@}*/
 
    /**
diff --git a/src/glsl/ir.h b/src/glsl/ir.h
index e614858..f586025 100644
--- a/src/glsl/ir.h
+++ b/src/glsl/ir.h
@@ -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
index 0000000..2655444
--- /dev/null
+++ b/src/glsl/ir_dead_functions.cpp
@@ -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;
+}
diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h
index 55ec327..e0c0715 100644
--- a/src/glsl/ir_optimization.h
+++ b/src/glsl/ir_optimization.h
@@ -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);
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index 94db57d..f9e24ca 100644
--- a/src/glsl/linker.cpp
+++ b/src/glsl/linker.cpp
@@ -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;




More information about the mesa-commit mailing list