From: Vincent Lejeune <v...@ovi.com>

The algorithm can spot non adjacent operands.
Limitations :
- only works on basic block
- only works on binary expressions
---
 src/glsl/Makefile                                 |    1 +
 src/glsl/glsl_parser_extras.cpp                   |    7 +-
 src/glsl/ir_optimization.h                        |    1 +
 src/glsl/list.h                                   |    4 +-
 src/glsl/opt_common_subexpression_elimination.cpp |  665 +++++++++++++++++++++
 5 files changed, 674 insertions(+), 4 deletions(-)
 create mode 100644 src/glsl/opt_common_subexpression_elimination.cpp

diff --git a/src/glsl/Makefile b/src/glsl/Makefile
index 4100414..68b98b2 100644
--- a/src/glsl/Makefile
+++ b/src/glsl/Makefile
@@ -83,6 +83,7 @@ CXX_SOURCES = \
        opt_structure_splitting.cpp \
        opt_swizzle_swizzle.cpp \
        opt_tree_grafting.cpp \
+       opt_common_subexpression_elimination.cpp \
        s_expression.cpp
 
 LIBS = \
diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp
index d9aa300..0a57386 100644
--- a/src/glsl/glsl_parser_extras.cpp
+++ b/src/glsl/glsl_parser_extras.cpp
@@ -776,7 +776,9 @@ do_common_optimization(exec_list *ir, bool linked, unsigned 
max_unroll_iteration
 {
    GLboolean progress = GL_FALSE;
 
-   progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress;
+
+   progress = do_common_subexpression_elimination(ir) || progress;
+   /*progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress;
 
    if (linked) {
       progress = do_function_inlining(ir) || progress;
@@ -807,12 +809,13 @@ do_common_optimization(exec_list *ir, bool linked, 
unsigned max_unroll_iteration
 
    progress = optimize_redundant_jumps(ir) || progress;
 
+
    loop_state *ls = analyze_loop_variables(ir);
    if (ls->loop_found) {
       progress = set_loop_controls(ir, ls) || progress;
       progress = unroll_loops(ir, ls, max_unroll_iterations) || progress;
    }
-   delete ls;
+   delete ls;*/
 
    return progress;
 }
diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h
index dd26567..3604e4e 100644
--- a/src/glsl/ir_optimization.h
+++ b/src/glsl/ir_optimization.h
@@ -71,3 +71,4 @@ bool lower_variable_index_to_cond_assign(exec_list 
*instructions,
     bool lower_input, bool lower_output, bool lower_temp, bool lower_uniform);
 bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz);
 bool optimize_redundant_jumps(exec_list *instructions);
+bool do_common_subexpression_elimination(exec_list *instructions);
diff --git a/src/glsl/list.h b/src/glsl/list.h
index 1d46365..2478ab3 100644
--- a/src/glsl/list.h
+++ b/src/glsl/list.h
@@ -122,8 +122,8 @@ struct exec_node {
 
    void remove()
    {
-      next->prev = prev;
-      prev->next = next;
+         next->prev = prev;
+         prev->next = next;
       next = NULL;
       prev = NULL;
    }
diff --git a/src/glsl/opt_common_subexpression_elimination.cpp 
b/src/glsl/opt_common_subexpression_elimination.cpp
new file mode 100644
index 0000000..a360ecf
--- /dev/null
+++ b/src/glsl/opt_common_subexpression_elimination.cpp
@@ -0,0 +1,665 @@
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_hierarchical_visitor.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+#include <iostream>
+#include <cstring>
+using namespace std;
+
+/*
+ * Do local CSE at the moment
+ */
+
+typedef struct _pattern
+{
+       ir_variable* operand1;
+       ir_expression_operation operation;
+       ir_variable* operand2;
+} pattern;
+
+void display_pattern(const pattern& p)
+{
+       cout<<p.operand1->name << p.operation<< p.operand2->name <<endl;
+}
+
+int list_size(const exec_list* lst)
+{
+       int result=0;
+       foreach_list_const(tmp,lst)
+       {
+               result++;
+       }
+       return result;
+}
+
+class element : public exec_node
+{
+public:
+       pattern element_pattern;
+       exec_list* dereferences;
+
+       element(pattern p,void* mem_ctx):element_pattern(p)
+       {
+               dereferences = new (mem_ctx) exec_list();
+       }
+
+       ~element()
+       {
+
+       }
+};
+
+class box:public exec_node
+{
+public:
+       void* content;
+
+       box(void* c):content(c)
+       {
+
+       }
+};
+
+bool find(exec_list* lst,void* ptr)
+{
+       foreach_list_const(tmp,lst)
+       {
+               if(tmp == ptr)
+                       return true;
+       }
+       return false;
+}
+
+class list_pattern
+{
+       friend class expressions_recorder;
+protected:
+       void* mem_ctx;
+       exec_list* inner_list; // element* list
+
+       bool equal_pattern(pattern p1, pattern p2) const
+       {
+               return p1.operation == p2.operation && p1.operand1 == 
p2.operand1 && p1.operand2 == p2.operand2;
+       }
+
+       element* select_by_pattern(pattern p)
+       {
+               foreach_list(iterator,inner_list)
+               {
+                       element* current_node = (element*) iterator;
+                       if(equal_pattern(p,current_node->element_pattern))
+                               return current_node;
+               }
+               element* new_item = new (mem_ctx) element(p,mem_ctx);
+               inner_list->push_head(new_item);
+               return new_item;
+       }
+
+public:
+
+       bool is_present(pattern p) const
+       {
+               foreach_list_const(iterator,inner_list)
+               {
+                       element* current_node = (element*) iterator;
+                       if(equal_pattern(p,current_node->element_pattern))
+                               return true;
+               }
+               return false;
+       }
+
+       exec_list* get_dereferences_discarded(ir_variable* var)
+       {
+               exec_list* extracted_list_pattern = new(mem_ctx) exec_list();
+               foreach_list_safe(iterator,inner_list)
+               {
+                       element* current_node = (element*) iterator;
+                       pattern &p = current_node->element_pattern;
+
+                       if(p.operand1->name == var->name || p.operand2->name == 
var->name)
+                       {
+                               cout<<var->name <<" against ";
+                               display_pattern(p);
+                               extracted_list_pattern->push_tail(new (mem_ctx) 
box(current_node));
+                               current_node->remove();
+                       }
+               }
+
+               return extracted_list_pattern;
+       }
+
+       exec_list* at(pattern p)
+       {
+               return select_by_pattern(p)->dereferences;
+       }
+
+       void init()
+       {
+               inner_list = new (this) exec_list();
+       }
+
+};
+
+class expressions_recorder
+{
+       friend class ir_cse_visitor;
+
+private:
+       list_pattern* binop_container;
+
+       inline
+       pattern get_pattern(ir_expression_operation op,ir_variable* 
var1,ir_variable* var2) const
+       {
+               char* c1 = const_cast<char*>(var1->name);
+               char* c2 = const_cast<char*>(var2->name);
+               int res = strcmp(c1,c2);
+               if(res>0)
+               {
+                       pattern result = {var1,op,var2};
+                       return result;
+               }
+               else
+               {
+                       pattern result = {var2,op,var1};
+                       return result;
+               }
+       }
+
+public:
+       bool expression_already_seen( ir_variable* var1,ir_expression_operation 
op, ir_variable* var2) const
+       {
+               pattern p = get_pattern(op,var1,var2);
+               return binop_container->is_present(p);
+       }
+
+       bool add_expression(ir_dereference_variable *& 
var1,ir_expression_operation op,  ir_dereference_variable *& var2)
+       {
+               pattern p = get_pattern(op,var1->var,var2->var);
+               exec_list* dref_for_pattern = binop_container->at(p);
+               foreach_list_const(it,dref_for_pattern)
+               {
+                       ir_dereference_variable* itref = 
(ir_dereference_variable*) ((box*)it)->content;
+                       if(itref==var1 || itref==var2)
+                       {
+                               return false;
+                       }
+               }
+               dref_for_pattern->push_tail(new (var1) box(var1));
+               dref_for_pattern->push_tail(new (var2) box(var2));
+
+               return true;
+       }
+
+       exec_list* kill_variable(ir_variable* var)
+       {
+               return binop_container->get_dereferences_discarded(var);
+       }
+
+       exec_list* flush()
+       {
+               return binop_container->inner_list;
+       }
+
+       void init()
+       {
+               binop_container = (list_pattern*) 
ralloc_size(this,sizeof(list_pattern));
+               binop_container->init();
+       }
+
+
+};
+
+class ir_flattened_expression : public ir_expression
+{
+protected:
+
+       ir_rvalue* recursive_deep_parse(ir_rvalue* ir) const
+       {
+               if(ir_expression* expr = ir->as_expression())
+               {
+                       return new(expr) ir_flattened_expression(expr);
+               }
+               return ir;
+       }
+
+       void recursive_breadth_parse(ir_expression* ir)
+       {
+               if(operation == ir->operation)
+               {
+                       for(unsigned i=0;i<ir->get_num_operands();i++)
+                       {
+                               ir_expression* expr = 
ir->operands[i]->as_expression();
+                               if(expr)
+                               {
+                                       recursive_breadth_parse(expr);
+                               }
+                               else
+                               {
+                                       box* boxed_rvalue = new (ir) box 
(recursive_deep_parse(ir->operands[i]));
+                                       operators->push_tail( boxed_rvalue );
+                               }
+                       }
+               }
+               else
+               {
+                       box* boxed_rvalue = new (ir) box 
(recursive_deep_parse(ir));
+                       operators->push_tail(boxed_rvalue);
+               }
+       }
+
+       ir_rvalue* to_classic_expr(ir_rvalue* ir) const
+       {
+               if(ir_expression* expr = ir->as_expression())
+               {
+                       ir_flattened_expression* fexpr = 
dynamic_cast<ir_flattened_expression*>(expr);
+                       return fexpr->to_classic_expression();
+               }
+               else
+                       return ir;
+       }
+
+
+       bool find(element* el,ir_dereference_variable* 
vard1,ir_dereference_variable* vard2) const
+       {
+               exec_list* derefs = el->dereferences;
+               bool has_v1 = false,has_v2 = false;
+               foreach_list_const(it,derefs)
+               {
+                       ir_dereference_variable* itref = 
(ir_dereference_variable*)((box*)it)->content;
+                       if(itref==vard1)
+                               has_v1=true;
+                       if(itref==vard2)
+                               has_v2=true;
+               }
+               return has_v1 && has_v2;
+       }
+
+       bool remove(ir_variable* newvar,element* el,exec_node*& ndi,exec_node*& 
ndj)
+       {
+               foreach_list_safe(node_i,operators)
+               {
+                       foreach_list_safe(node_j,operators)
+                       {
+                               ir_rvalue* it_i = (ir_rvalue*) 
((box*)node_i)->content;
+                               ir_rvalue* it_j = (ir_rvalue*) 
((box*)node_j)->content;
+                               ir_dereference_variable* var1 = 
it_i->as_dereference_variable();
+                               ir_dereference_variable* var2 = 
it_j->as_dereference_variable();
+                               if(!var1 || !var2 || it_i==it_j)
+                                       continue;
+
+                               if( find(el,var1,var2) )
+                               {
+                                       ir_dereference_variable* newvar_ref = 
new(newvar) ir_dereference_variable(newvar);
+                                       box* boxed_value = new (newvar_ref) 
box(newvar_ref);
+                                       operators->push_tail(boxed_value);
+                                       ndi = node_i;
+                                       ndj = node_j;
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+
+       void eliminate_expr_flatten(ir_variable* newvar,element* el,bool& 
is_modified)
+       {
+               bool boolflag;
+               is_modified = false;
+               if(operation != el->element_pattern.operation)
+                       return;
+
+               exec_node *node_i,*node_j;
+               boolflag = remove(newvar,el,node_i,node_j);
+               is_modified = boolflag;
+               while(boolflag)
+               {
+                       node_i->remove();
+                       node_j->remove();
+                       boolflag = remove(newvar,el,node_i,node_j);
+               }
+       }
+
+public:
+       exec_list* operators;
+       ir_flattened_expression(ir_expression* expr):ir_expression(*expr)
+       {
+               operators = new (expr) exec_list();
+               recursive_breadth_parse(expr);
+       }
+
+       virtual ~ir_flattened_expression()
+       {
+
+       }
+
+       exec_list* get_dereference_variable() const
+       {
+               exec_list* result = new (operators) exec_list();
+
+               foreach_list_const(node,operators)
+               {
+                       ir_rvalue* it = (ir_rvalue*) ((box*)node)->content;
+                       if(ir_dereference_variable* 
var=it->as_dereference_variable())
+                       {
+                               result->push_tail(new (result) box(var));
+                       }
+               }
+               return result;
+       }
+
+       exec_list* get_expressions() const
+       {
+               exec_list* result = new (operators) exec_list();
+               foreach_list_const(node,operators)
+               {
+                       ir_rvalue* it = (ir_rvalue*)((box*)node)->content;
+                       if(ir_expression* expr = it->as_expression())
+                       {
+                               ir_flattened_expression* fexpr = 
dynamic_cast<ir_flattened_expression*>(expr);
+                               result->push_tail(new (result) box(fexpr));
+                       }
+               }
+               return result;
+
+       }
+
+       ir_rvalue* to_classic_expression() const
+       {
+               int step=-1;
+               box* tmp = (box*)operators->get_head();
+               ir_rvalue* current_tree = 
to_classic_expr((ir_rvalue*)tmp->content);
+               foreach_list_const(node,operators)
+               {
+                       step++;
+                       ir_rvalue* it = (ir_rvalue*) ((box*)node)->content;
+                       if(!step)
+                               continue;
+                       ir_rvalue* current_rvalue = to_classic_expr(it);
+                       ir_rvalue* temp_tree = new(current_tree) 
ir_expression(operation,current_rvalue,current_tree);
+                       current_tree = temp_tree;
+               }
+               return current_tree;
+       }
+
+       void eliminate_expr(ir_variable* newvar,element* el,bool& is_modified)
+       {
+               eliminate_expr_flatten(newvar,el,is_modified);
+               foreach_list_safe(node,operators)
+               {
+                       bool child_is_modified;
+                       ir_rvalue* it = (ir_rvalue*) ((box*)node)->content;
+                       ir_expression* expr = it->as_expression();
+                       if(!expr)
+                               continue;
+                       ir_flattened_expression* fexpr = 
dynamic_cast<ir_flattened_expression*>(expr);
+                       fexpr->eliminate_expr(newvar,el,child_is_modified);
+
+                       bool size_equal_1 = operators->head && 
!operators->head->next;
+                       if( size_equal_1 )
+                       {
+                               ir_rvalue* tmp = 
(ir_rvalue*)((box*)(fexpr->operators->head))->content;
+                               operators->push_tail(new (tmp) box(tmp));
+                               node->remove();
+                               cout<<"unique"<<endl;
+                               child_is_modified = true;
+                       }
+                       is_modified = is_modified || child_is_modified;
+
+               }
+       }
+
+       void display() const
+       {
+               cout<<"OP:"<<operation<<endl;
+               cout<<"VARIABLE:"<<endl;
+               exec_list* vars = get_dereference_variable();
+               foreach_list_const(it,vars)
+               {
+                       ir_dereference_variable* dref = 
(ir_dereference_variable*) ((box*)it)->content;
+                       cout<< dref->var->name << " ";
+               }
+               cout<<endl;
+
+               exec_list* exprs = get_expressions();
+               foreach_list_const(it,exprs)
+               {
+                       cout<<"LEAVES OF"<<operation<<":"<<endl;
+                       ir_flattened_expression* fexpr = 
(ir_flattened_expression*) ((box*)it)->content;
+                       fexpr->display();
+               }
+
+       }
+};
+
+class ir_cse_visitor: public ir_hierarchical_visitor
+{
+
+private:
+       void find_cse_at_same_depth(const ir_flattened_expression*);
+
+protected:
+       expressions_recorder* available_expressions;
+       exec_list* candidates;
+       void discover_cse(const ir_flattened_expression*);
+
+public:
+       ir_cse_visitor(void* mem_ctx);
+
+   virtual ir_visitor_status visit_enter(class ir_assignment*);
+   virtual ir_visitor_status visit_leave(class ir_assignment*);
+   exec_list* get_candidate();
+};
+
+void ir_cse_visitor::find_cse_at_same_depth(const ir_flattened_expression* 
expr)
+{
+       exec_list* variables = expr->get_dereference_variable();
+       ir_expression_operation op = expr->operation;
+       foreach_list_const(var_i,variables)
+       {
+               foreach_list_const(var_j,variables)
+               {
+                       if(var_i != var_j)
+                       {
+                               ir_dereference_variable* var1 = 
(ir_dereference_variable*) ((box*)var_i)->content;
+                               ir_dereference_variable* var2 = 
(ir_dereference_variable*) ((box*)var_j)->content;
+
+                               
available_expressions->add_expression(var1,op,var2);
+                       }
+               }
+       }
+}
+
+
+
+void ir_cse_visitor::discover_cse(const ir_flattened_expression* expr)
+{
+       find_cse_at_same_depth(expr);
+       exec_list* exprs = expr->get_expressions();
+       foreach_list_const(it,exprs)
+       {
+               ir_flattened_expression* it_expr = (ir_flattened_expression*) 
((box*)it)->content;
+               discover_cse(it_expr);
+       }
+}
+
+
+
+ir_cse_visitor::ir_cse_visitor(void* mem_ctx):ir_hierarchical_visitor()
+{
+       candidates = new (mem_ctx) exec_list();
+       available_expressions = (expressions_recorder*) 
ralloc_size(mem_ctx,sizeof(expressions_recorder));
+       available_expressions->init();
+}
+
+ir_visitor_status ir_cse_visitor::visit_enter(ir_assignment* ir)
+{
+
+       if(ir_expression* expr = ir->rhs->as_expression())
+       {
+               ir_flattened_expression* fexpr = new(ir) 
ir_flattened_expression(expr);;
+               discover_cse(fexpr);
+               return visit_continue;
+       }
+       else
+       {
+               return visit_continue;
+       }
+}
+
+ir_visitor_status ir_cse_visitor::visit_leave(ir_assignment* ir)
+{
+       ir_variable* var = ir->lhs->as_dereference_variable()->var;
+       cout<<"removing : "<<var->name<<endl;
+       exec_list* tmplst = available_expressions->kill_variable(var);
+       foreach_list_const(tmp,tmplst)
+       {
+               element* c = (element*)((box*)tmp)->content;
+               candidates->push_tail(new (c) box(c));
+       }
+       return visit_continue;
+}
+
+exec_list* ir_cse_visitor::get_candidate()
+{
+       exec_list* tmplst = available_expressions->flush();
+       if(tmplst)
+       {
+               foreach_list_const(tmp,tmplst)
+               {
+                       element* element_tmp = (element*) tmp;
+                       candidates->push_tail(new (element_tmp) 
box(element_tmp));
+               }
+       }
+       exec_list* result = new (tmplst) exec_list();
+
+       foreach_list_const(candidate,candidates)
+       {
+               element* candidate_element = (element*) 
((box*)candidate)->content;
+               if(list_size(candidate_element->dereferences)>2)
+               {
+                       result->push_tail(new (candidate_element) box 
(candidate_element));
+               }
+       }
+
+       return result;
+}
+
+class ir_cse_factoriser: public ir_hierarchical_visitor
+{
+protected:
+       element* element_to_remove;
+       ir_variable* newvar;
+       bool activated;
+public:
+       ir_visitor_status visit_enter(ir_assignment* ir);
+       ir_cse_factoriser(element*);
+private:
+    void init_newvar(ir_assignment *& ir);
+    void assign_newvar(ir_assignment* ir);
+};
+
+void ir_cse_factoriser::init_newvar(ir_assignment *& ir)
+{
+       if(!newvar)
+       {
+               pattern p = element_to_remove->element_pattern;
+               newvar = new(ir) 
ir_variable(p.operand1->type,"tmp",ir_var_temporary);
+               ir->insert_before(newvar);
+
+       }
+}
+
+void ir_cse_factoriser::assign_newvar(ir_assignment* ir)
+{
+       pattern p = element_to_remove->element_pattern;
+       ir_expression* factored = new(ir) ir_expression(p.operation,new (ir) 
ir_dereference_variable(p.operand1),new (ir) 
ir_dereference_variable(p.operand2));
+       ir_assignment* assign = new(ir) ir_assignment(new(ir) 
ir_dereference_variable(newvar),factored,NULL);
+       ir->insert_before(assign);
+}
+
+ir_visitor_status
+ir_cse_factoriser::visit_enter(ir_assignment* ir)
+{
+       init_newvar(ir);
+       ir_expression* expr = ir->rhs->as_expression();
+       if(!expr)
+               return visit_continue;
+       ir_flattened_expression* fexpr = new(ir) ir_flattened_expression(expr);
+
+       bool is_modified;
+       fexpr->eliminate_expr(newvar,element_to_remove,is_modified);
+       if(!activated && is_modified)
+       {
+               assign_newvar(ir);
+               activated = true;
+       }
+
+       ir->rhs = fexpr->to_classic_expression();
+       return visit_continue;
+}
+
+ir_cse_factoriser::ir_cse_factoriser(element* 
e):element_to_remove(e),newvar(NULL),activated(false)
+{
+
+}
+
+
+
+
+
+static void
+cse_basic_block(ir_instruction *first,
+                            ir_instruction *last,
+                            void *data)
+{
+       ir_instruction *ir, *ir_next;
+       ir_cse_visitor* visitor = new ir_cse_visitor(first);
+       for (ir = first, ir_next = (ir_instruction *)first->next;;
+               ir = ir_next, ir_next = (ir_instruction *)ir->next)
+       {
+
+               ir->accept(visitor);
+               if(ir==last)
+                       break;
+       }
+       exec_list* candidates = visitor->get_candidate();
+       foreach_list_const(it,candidates)
+       {
+               element* it_el = (element*) ((box*)it)->content;
+               pattern tmp = it_el->element_pattern;
+               cout<<"CANDIDATE:";
+               display_pattern(tmp);
+               //cout << (*it)->dereferences->size()/2<<endl;
+               ir_cse_factoriser factoriser(it_el);
+               for (ir = first, ir_next = (ir_instruction *)first->next;;
+                       ir = ir_next, ir_next = (ir_instruction *)ir->next)
+               {
+
+                       ir->accept(&factoriser);
+                       if(ir==last)
+                               break;
+               }
+       }
+}
+
+static int occurrence=0;
+
+bool
+do_common_subexpression_elimination(exec_list *instructions)
+{
+
+   call_for_basic_blocks(instructions,cse_basic_block,NULL);
+
+   if(occurrence++==0)
+          return true;
+   /*if(!already_entered)
+          {
+          already_entered = true;
+          return true;
+          }*/
+   return false;
+}
-- 
1.7.3.4

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to