diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | glsl_parser_extras.cpp | 2 | ||||
-rw-r--r-- | ir.h | 12 | ||||
-rw-r--r-- | ir_dead_code.cpp | 336 | ||||
-rw-r--r-- | ir_dead_code.h | 32 |
5 files changed, 383 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 1ef1a0266b2..80b5c2ec680 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,6 +30,7 @@ glsl_SOURCES = symbol_table.c hash_table.c glsl_types.cpp \ ir_print_visitor.cpp ir_variable.cpp ir_function.cpp \ ir_constant_expression.cpp \ ir_constant_folding.cpp \ + ir_dead_code.cpp \ ir_expression_flattening.cpp \ ir_function_inlining.cpp \ ir_if_simplification.cpp diff --git a/glsl_parser_extras.cpp b/glsl_parser_extras.cpp index 455bf0c7a4a..f7ee891eeb6 100644 --- a/glsl_parser_extras.cpp +++ b/glsl_parser_extras.cpp @@ -35,6 +35,7 @@ #include "glsl_parser_extras.h" #include "glsl_parser.h" #include "ir_constant_folding.h" +#include "ir_dead_code.h" #include "ir_function_inlining.h" #include "ir_if_simplification.h" #include "ir_print_visitor.h" @@ -761,6 +762,7 @@ main(int argc, char **argv) progress = do_function_inlining(&instructions) || progress; progress = do_if_simplification(&instructions) || progress; + progress = do_dead_code_unlinked(&instructions) || progress; /* Constant folding */ ir_constant_folding_visitor constant_folding; @@ -54,11 +54,13 @@ public: virtual class ir_variable * as_variable() { return NULL; } virtual class ir_dereference * as_dereference() { return NULL; } virtual class ir_rvalue * as_rvalue() { return NULL; } + virtual class ir_label * as_label() { return NULL; } virtual class ir_loop * as_loop() { return NULL; } virtual class ir_assignment * as_assignment() { return NULL; } virtual class ir_call * as_call() { return NULL; } virtual class ir_return * as_return() { return NULL; } virtual class ir_if * as_if() { return NULL; } + virtual class ir_swizzle * as_swizzle() { return NULL; } /*@}*/ protected: @@ -185,6 +187,11 @@ class ir_label : public ir_instruction { public: ir_label(const char *label, ir_function_signature *signature); + virtual ir_label *as_label() + { + return this; + } + virtual void accept(ir_visitor *v) { v->visit(this); @@ -681,6 +688,11 @@ public: /* empty */ } + virtual ir_swizzle *as_swizzle() + { + return this; + } + ir_swizzle *clone() { return new ir_swizzle(this->val, this->mask); diff --git a/ir_dead_code.cpp b/ir_dead_code.cpp new file mode 100644 index 00000000000..aae45d993ea --- /dev/null +++ b/ir_dead_code.cpp @@ -0,0 +1,336 @@ +/* + * 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_code.cpp + * + * Eliminates dead assignments and variable declarations from the code. + */ + +#define NULL 0 +#include "ir.h" +#include "ir_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" + +class variable_entry : public exec_node +{ +public: + variable_entry(ir_variable *var) + { + this->var = var; + assign = NULL; + referenced = false; + declaration = false; + } + + ir_variable *var; /* The key: the variable's pointer. */ + ir_assignment *assign; /* An assignment to the variable, if any */ + bool referenced; /* If the variable has ever been referenced. */ + bool declaration; /* If the variable had a decl in the instruction stream */ +}; + +class ir_dead_code_visitor : public ir_visitor { +public: + + /** + * \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_label *); + 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 *); + /*@}*/ + + variable_entry *get_variable_entry(ir_variable *var); + + bool (*predicate)(ir_instruction *ir); + ir_instruction *base_ir; + + /* List of variable_entry */ + exec_list variable_list; +}; + +variable_entry * +ir_dead_code_visitor::get_variable_entry(ir_variable *var) +{ + assert(var); + foreach_iter(exec_list_iterator, iter, this->variable_list) { + variable_entry *entry = (variable_entry *)iter.get(); + if (entry->var == var) + return entry; + } + + variable_entry *entry = new variable_entry(var); + this->variable_list.push_tail(entry); + return entry; +} + +void +find_dead_code(exec_list *instructions, ir_dead_code_visitor *v) +{ + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + ir->accept(v); + } +} + +void +ir_dead_code_visitor::visit(ir_variable *ir) +{ + variable_entry *entry = this->get_variable_entry(ir); + if (entry) { + entry->declaration = true; + } +} + + +void +ir_dead_code_visitor::visit(ir_label *ir) +{ + ir->signature->accept(this); +} + +void +ir_dead_code_visitor::visit(ir_loop *ir) +{ + find_dead_code(&ir->body_instructions, this); + if (ir->from) + ir->from->accept(this); + if (ir->to) + ir->to->accept(this); + if (ir->increment) + ir->increment->accept(this); +} + +void +ir_dead_code_visitor::visit(ir_loop_jump *ir) +{ + (void) ir; +} + + +void +ir_dead_code_visitor::visit(ir_function_signature *ir) +{ + find_dead_code(&ir->body, this); +} + +void +ir_dead_code_visitor::visit(ir_function *ir) +{ + (void) ir; +} + +void +ir_dead_code_visitor::visit(ir_expression *ir) +{ + unsigned int operand; + + for (operand = 0; operand < ir->get_num_operands(); operand++) { + ir->operands[operand]->accept(this); + } +} + + +void +ir_dead_code_visitor::visit(ir_swizzle *ir) +{ + ir->val->accept(this); +} + + +void +ir_dead_code_visitor::visit(ir_dereference *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) { + variable_entry *entry = this->get_variable_entry(var); + entry->referenced = true; + } else { + ir->var->accept(this); + } +} + +void +ir_dead_code_visitor::visit(ir_assignment *ir) +{ + ir_instruction *lhs = ir->lhs; + + /* Walk through the LHS and mark references for variables used in + * array indices but not for the assignment dereference. + */ + while (lhs) { + if (lhs->as_variable()) + break; + + ir_dereference *deref = lhs->as_dereference(); + if (deref) { + if (deref->mode == ir_dereference::ir_reference_array) + deref->selector.array_index->accept(this); + lhs = deref->var; + } else { + ir_swizzle *swiz = lhs->as_swizzle(); + + lhs = swiz->val; + } + } + + ir->rhs->accept(this); + if (ir->condition) + ir->condition->accept(this); + + variable_entry *entry; + entry = this->get_variable_entry(lhs->as_variable()); + if (entry) { + if (entry->assign == NULL) + entry->assign = ir; + } +} + + +void +ir_dead_code_visitor::visit(ir_constant *ir) +{ + (void) ir; +} + + +void +ir_dead_code_visitor::visit(ir_call *ir) +{ + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param = (ir_rvalue *)iter.get(); + + /* FINISHME: handle out values. */ + param->accept(this); + } + + /* Ignore the callee. Function bodies will get handled when they're + * encountered at the top level instruction stream and spawn their + * own dead code visitor. + */ +} + + +void +ir_dead_code_visitor::visit(ir_return *ir) +{ + ir->get_value()->accept(this); +} + + +void +ir_dead_code_visitor::visit(ir_if *ir) +{ + ir->condition->accept(this); + + find_dead_code(&ir->then_instructions, this); + find_dead_code(&ir->else_instructions, this); +} + +/** + * Do a dead code pass over instructions and everything that instructions + * references. + * + * Note that this will remove assignments to globals, so it is not suitable + * for usage on an unlinked instruction stream. + */ +bool +do_dead_code(exec_list *instructions) +{ + ir_dead_code_visitor v; + bool progress = false; + + find_dead_code(instructions, &v); + + foreach_iter(exec_list_iterator, iter, v.variable_list) { + variable_entry *entry = (variable_entry *)iter.get(); + + if (entry->referenced || !entry->declaration) + continue; + + if (entry->assign) { + /* Remove a single dead assignment to the variable we found. + * Don't do so if it's a shader output, though. + */ + if (!entry->var->shader_out) { + entry->assign->remove(); + progress = true; + } + } else { + /* If there are no assignments or references to the variable left, + * then we can remove its declaration. + */ + entry->var->remove(); + progress = true; + } + } + return progress; +} + +/** + * Does a dead code pass on the functions present in the instruction stream. + * + * This is suitable for use while the program is not linked, as it will + * ignore variable declarations (and the assignments to them) for variables + * with global scope. + */ +bool +do_dead_code_unlinked(exec_list *instructions) +{ + bool progress = false; + + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_label *label = ir->as_label(); + if (label) { + if (do_dead_code(&label->signature->body)) + progress = true; + } + } + + return progress; +} diff --git a/ir_dead_code.h b/ir_dead_code.h new file mode 100644 index 00000000000..25bf6f6256c --- /dev/null +++ b/ir_dead_code.h @@ -0,0 +1,32 @@ +/* + * 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_code.h + * + * Eliminates dead assignments and variable declarations from the code. + */ + +bool do_dead_code(exec_list *instructions); +bool do_dead_code_unlinked(exec_list *instructions); |