]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/optimize.cpp
Redesign ExpressionInliner
[libs/gl.git] / source / glsl / optimize.cpp
index 55e5da5fddeec0896cbd52b087a87bf928da305d..2e7f8aa733b5b9acda8bdb0e1529930e3e7a01e0 100644 (file)
@@ -351,9 +351,9 @@ void FunctionInliner::visit(Iteration &iter)
 
 ExpressionInliner::ExpressionInliner():
        r_ref_info(0),
-       r_any_inlined(false),
        r_trivial(false),
-       mutating(false),
+       access_read(true),
+       access_write(false),
        iteration_init(false),
        iteration_body(0),
        r_oper(0)
@@ -362,62 +362,48 @@ ExpressionInliner::ExpressionInliner():
 bool ExpressionInliner::apply(Stage &s)
 {
        s.content.visit(*this);
-       return r_any_inlined;
-}
-
-void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr)
-{
-       ptr = expr.clone();
-       r_any_inlined = true;
-}
-
-void ExpressionInliner::visit(Block &block)
-{
-       TraversingVisitor::visit(block);
 
-       for(map<string, VariableDeclaration *>::iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
-       {
-               map<Assignment::Target, ExpressionInfo>::iterator j = expressions.lower_bound(i->second);
-               for(; (j!=expressions.end() && j->first.declaration==i->second); )
+       bool any_inlined = false;
+       for(list<ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
+               if(i->expression && (i->trivial || i->uses.size()==1))
                {
-                       if(j->second.expression && j->second.inline_point)
-                               inline_expression(*j->second.expression, *j->second.inline_point);
-
-                       expressions.erase(j++);
+                       for(vector<ExpressionUse>::iterator j=i->uses.begin(); j!=i->uses.end(); ++j)
+                               if(!j->blocked)
+                               {
+                                       *j->reference = i->expression->clone();
+                                       any_inlined = true;
+                               }
                }
-       }
 
-       /* Expressions assigned in this block may depend on local variables of the
-       block.  If this is a conditionally executed block, the assignments might not
-       always happen.  Mark the expressions as not available to any outer blocks. */
-       for(map<Assignment::Target, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
-               if(i->second.assign_scope==&block)
-                       i->second.available = false;
+       return any_inlined;
 }
 
 void ExpressionInliner::visit(RefPtr<Expression> &expr)
 {
        r_ref_info = 0;
        expr->visit(*this);
-       if(r_ref_info && r_ref_info->expression && r_ref_info->available)
+       if(r_ref_info && r_ref_info->expression)
        {
+               ExpressionUse use;
+               use.reference = &expr;
+               use.ref_scope = current_block;
+               use.blocked = access_write;
+
                if(iteration_body && !r_ref_info->trivial)
                {
-                       /* Don't inline non-trivial expressions which were assigned outside
-                       an iteration statement.  The iteration may run multiple times, which
+                       /* Block inlining of non-trivial expressions assigned outside an
+                       iteration statement.  The iteration may run multiple times, which
                        would cause the expression to also be evaluated multiple times. */
-                       Block *i = r_ref_info->assign_scope;
-                       for(; (i && i!=iteration_body); i=i->parent) ;
-                       if(!i)
-                               return;
+                       for(Block *i=iteration_body->parent; (!use.blocked && i); i=i->parent)
+                               use.blocked = (i==r_ref_info->assign_scope);
                }
 
-               if(r_ref_info->trivial)
-                       inline_expression(*r_ref_info->expression, expr);
-               else
-                       /* Record the inline point for a non-trivial expression but don't
-                       inline it yet.  It might turn out it shouldn't be inlined after all. */
-                       r_ref_info->inline_point = &expr;
+               /* Block inlining assignments from from inner scopes.  The assignment may
+               depend on local variables of that scope or may not always be executed. */
+               for(Block *i=r_ref_info->assign_scope->parent; (!use.blocked && i); i=i->parent)
+                       use.blocked = (i==current_block);
+
+               r_ref_info->uses.push_back(use);
        }
        r_oper = expr->oper;
        r_ref_info = 0;
@@ -425,21 +411,11 @@ void ExpressionInliner::visit(RefPtr<Expression> &expr)
 
 void ExpressionInliner::visit(VariableReference &var)
 {
-       if(var.declaration)
+       if(var.declaration && access_read)
        {
-               map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(var.declaration);
-               if(i!=expressions.end())
-               {
-                       /* If a non-trivial expression is referenced multiple times, don't
-                       inline it. */
-                       if(i->second.inline_point && !i->second.trivial)
-                               i->second.expression = 0;
-                       /* Mutating expressions are analogous to self-referencing assignments
-                       and prevent inlining. */
-                       if(mutating)
-                               i->second.expression = 0;
-                       r_ref_info = &i->second;
-               }
+               map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(var.declaration);
+               if(i!=assignments.end())
+                       r_ref_info = i->second;
        }
 }
 
@@ -457,7 +433,7 @@ void ExpressionInliner::visit(Swizzle &swizzle)
 
 void ExpressionInliner::visit(UnaryExpression &unary)
 {
-       SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
+       SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
        visit(unary.expression);
        r_trivial = false;
 }
@@ -466,7 +442,7 @@ void ExpressionInliner::visit(BinaryExpression &binary)
 {
        visit(binary.left);
        {
-               SetFlag clear_target(mutating, false);
+               SetFlag clear_write(access_write, false);
                visit(binary.right);
        }
        r_trivial = false;
@@ -475,21 +451,37 @@ void ExpressionInliner::visit(BinaryExpression &binary)
 void ExpressionInliner::visit(Assignment &assign)
 {
        {
-               SetFlag set_target(mutating);
+               SetFlag set_read(access_read, assign.oper->token[0]!='=');
+               SetFlag set_write(access_write);
                visit(assign.left);
        }
        r_oper = 0;
+       r_trivial = true;
        visit(assign.right);
 
-       map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(assign.target);
-       if(i!=expressions.end())
+       map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(assign.target);
+       if(i!=assignments.end())
        {
-               /* Self-referencing assignments can't be inlined without additional
-               work.  Just clear any previous expression. */
-               i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
-               i->second.assign_scope = current_block;
-               i->second.inline_point = 0;
-               i->second.available = true;
+               if(iteration_body && i->second->expression)
+               {
+                       /* Block inlining into previous references within the iteration
+                       statement.  On iterations after the first they would refer to the
+                       assignment within the iteration. */
+                       for(vector<ExpressionUse>::iterator j=i->second->uses.begin(); j!=i->second->uses.end(); ++j)
+                               for(Block *k=j->ref_scope; (!j->blocked && k); k=k->parent)
+                                       j->blocked = (k==iteration_body);
+               }
+
+               expressions.push_back(ExpressionInfo());
+               ExpressionInfo &info = expressions.back();
+               info.target = assign.target;
+               // Self-referencing assignments can't be inlined without additional work.
+               if(!assign.self_referencing)
+                       info.expression = assign.right;
+               info.assign_scope = current_block;
+               info.trivial = r_trivial;
+
+               i->second = &info;
        }
 
        r_trivial = false;
@@ -527,12 +519,17 @@ void ExpressionInliner::visit(VariableDeclaration &var)
        analyze and non-trivial expressions could be expensive to inline.  */
        if((current_block->parent || (constant && r_trivial)) && var.interface.empty())
        {
-               ExpressionInfo &info = expressions[&var];
+               expressions.push_back(ExpressionInfo());
+               ExpressionInfo &info = expressions.back();
+               info.target = &var;
                /* Assume variables declared in an iteration initialization statement
                will have their values change throughout the iteration. */
-               info.expression = (iteration_init ? 0 : var.init_expression.get());
+               if(!iteration_init)
+                       info.expression = var.init_expression;
                info.assign_scope = current_block;
                info.trivial = r_trivial;
+
+               assignments[&var] = &info;
        }
 }