]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/optimize.cpp
Remove output variables which are not referenced
[libs/gl.git] / source / glsl / optimize.cpp
index 8c586e7ad886cf433c4c5cadf6315783980804a6..1e974c9f01f8eee6d3eea5faabc00b20759ffd37 100644 (file)
@@ -520,6 +520,209 @@ void ExpressionInliner::visit(Iteration &iter)
 }
 
 
+bool AggregateDismantler::apply(Stage &stage)
+{
+       stage.content.visit(*this);
+
+       bool any_dismantled = false;
+       for(const auto &kvp: aggregates)
+       {
+               if(kvp.second.referenced || !kvp.second.members_referenced)
+                       continue;
+
+               for(const AggregateMember &m: kvp.second.members)
+               {
+                       string name;
+                       if(m.declaration)
+                               name = format("%s_%s", kvp.second.declaration->name, m.declaration->name);
+                       else
+                               name = format("%s_%d", kvp.second.declaration->name, m.index);
+
+                       VariableDeclaration *var = new VariableDeclaration;
+                       var->source = kvp.first->source;
+                       var->line = kvp.first->line;
+                       var->name = get_unused_variable_name(*kvp.second.decl_scope, name);
+                       /* XXX This is kind of brittle and depends on the array declaration's
+                       textual type not having brackets in it. */
+                       var->type = (m.declaration ? m.declaration : kvp.second.declaration)->type;
+                       if(m.initializer)
+                               var->init_expression = m.initializer->clone();
+
+                       kvp.second.decl_scope->body.insert(kvp.second.insert_point, var);
+
+                       for(RefPtr<Expression> *r: m.references)
+                       {
+                               VariableReference *ref = new VariableReference;
+                               ref->name = var->name;
+                               *r = ref;
+                       }
+
+                       any_dismantled = true;
+               }
+       }
+
+       return any_dismantled;
+}
+
+void AggregateDismantler::visit(Block &block)
+{
+       SetForScope<Block *> set_block(current_block, &block);
+       for(auto i=block.body.begin(); i!=block.body.end(); ++i)
+       {
+               insert_point = i;
+               (*i)->visit(*this);
+       }
+}
+
+void AggregateDismantler::visit(RefPtr<Expression> &expr)
+{
+       r_aggregate_ref = 0;
+       expr->visit(*this);
+       if(r_aggregate_ref && r_reference.chain_len==1)
+       {
+               if((r_reference.chain[0]&0x3F)!=0x3F)
+               {
+                       r_aggregate_ref->members[r_reference.chain[0]&0x3F].references.push_back(&expr);
+                       r_aggregate_ref->members_referenced = true;
+               }
+               else
+                       /* If the accessed member is not known, mark the entire aggregate as
+                       referenced. */
+                       r_aggregate_ref->referenced = true;
+       }
+       r_aggregate_ref = 0;
+}
+
+void AggregateDismantler::visit(VariableReference &var)
+{
+       if(composite_reference)
+               r_reference.declaration = var.declaration;
+       else
+       {
+               /* If an aggregate variable is referenced as a whole, it should not be
+               dismantled. */
+               auto i = aggregates.find(var.declaration);
+               if(i!=aggregates.end())
+                       i->second.referenced = true;
+       }
+}
+
+void AggregateDismantler::visit_composite(RefPtr<Expression> &expr)
+{
+       if(!composite_reference)
+               r_reference = Assignment::Target();
+
+       SetFlag set_composite(composite_reference);
+       visit(expr);
+}
+
+void AggregateDismantler::visit(MemberAccess &memacc)
+{
+       visit_composite(memacc.left);
+
+       add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index);
+
+       if(r_reference.declaration && r_reference.chain_len==1)
+       {
+               auto i = aggregates.find(r_reference.declaration);
+               r_aggregate_ref = (i!=aggregates.end() ? &i->second : 0);
+       }
+       else
+               r_aggregate_ref = 0;
+}
+
+void AggregateDismantler::visit(BinaryExpression &binary)
+{
+       if(binary.oper->token[0]=='[')
+       {
+               visit_composite(binary.left);
+               {
+                       SetFlag clear_composite(composite_reference, false);
+                       visit(binary.right);
+               }
+
+               unsigned index = 0x3F;
+               if(Literal *literal_subscript = dynamic_cast<Literal *>(binary.right.get()))
+                       if(literal_subscript->value.check_type<int>())
+                               index = literal_subscript->value.value<int>();
+               add_to_chain(r_reference, Assignment::Target::ARRAY, index);
+
+               if(r_reference.declaration && r_reference.chain_len==1)
+               {
+                       auto i = aggregates.find(r_reference.declaration);
+                       r_aggregate_ref = (i!=aggregates.end() ? &i->second : 0);
+               }
+               else
+                       r_aggregate_ref = 0;
+       }
+       else
+       {
+               SetFlag clear_composite(composite_reference, false);
+               TraversingVisitor::visit(binary);
+       }
+}
+
+void AggregateDismantler::visit(VariableDeclaration &var)
+{
+       TraversingVisitor::visit(var);
+
+       if(var.interface.empty())
+       {
+               if(const StructDeclaration *strct = dynamic_cast<const StructDeclaration *>(var.type_declaration))
+               {
+                       const FunctionCall *init_call = dynamic_cast<const FunctionCall *>(var.init_expression.get());
+                       if((init_call && init_call->constructor) || !var.init_expression)
+                       {
+
+                               Aggregate &aggre = aggregates[&var];
+                               aggre.declaration = &var;
+                               aggre.decl_scope = current_block;
+                               aggre.insert_point = insert_point;
+
+                               unsigned i = 0;
+                               for(const RefPtr<Statement> &s: strct->members.body)
+                               {
+                                       if(const VariableDeclaration *mem_decl = dynamic_cast<const VariableDeclaration *>(s.get()))
+                                       {
+                                               AggregateMember member;
+                                               member.declaration = mem_decl;
+                                               member.index = i;
+                                               if(init_call)
+                                                       member.initializer = init_call->arguments[i];
+                                               aggre.members.push_back(member);
+                                       }
+                                       ++i;
+                               }
+                       }
+               }
+               else if(const Literal *literal_size = dynamic_cast<const Literal *>(var.array_size.get()))
+               {
+                       if(literal_size->value.check_type<int>())
+                       {
+                               Aggregate &aggre = aggregates[&var];
+                               aggre.declaration = &var;
+                               aggre.decl_scope = current_block;
+                               aggre.insert_point = insert_point;
+
+                               int size = literal_size->value.value<int>();
+                               for(int i=0; i<size; ++i)
+                               {
+                                       AggregateMember member;
+                                       member.index = i;
+                                       // Array initializers are not supported yet
+                                       aggre.members.push_back(member);
+                               }
+                       }
+               }
+       }
+}
+
+void AggregateDismantler::visit(FunctionDeclaration &func)
+{
+       func.body.visit(*this);
+}
+
+
 template<typename T>
 T ConstantFolder::evaluate_logical(char oper, T left, T right)
 {
@@ -1057,23 +1260,15 @@ bool UnusedVariableRemover::apply(Stage &s)
 
        for(const auto &kvp: variables)
        {
-               if(kvp.second.output)
+               if(!kvp.second.referenced)
+                       unused_nodes.insert(kvp.first);
+               else if(kvp.second.output)
                {
                        /* The last visible assignments of output variables are used by the
                        next stage or the API. */
                        for(AssignmentInfo *a: kvp.second.assignments)
                                unused_nodes.erase(a->node);
                }
-
-               if(!kvp.second.output && !kvp.second.referenced)
-               {
-                       // Don't remove variables from inside interface blocks.
-                       if(!kvp.second.interface_block)
-                               unused_nodes.insert(kvp.first);
-               }
-               else if(kvp.second.interface_block)
-                       // Interface blocks are kept if even one member is used.
-                       unused_nodes.erase(kvp.second.interface_block);
        }
 
        NodeRemover().apply(s, unused_nodes);
@@ -1095,10 +1290,10 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n
                        {
                                Assignment::Target::ChainType type1 = static_cast<Assignment::Target::ChainType>(a->target.chain[j]&0xC0);
                                Assignment::Target::ChainType type2 = static_cast<Assignment::Target::ChainType>(target.chain[j]&0xC0);
+                               unsigned index1 = a->target.chain[j]&0x3F;
+                               unsigned index2 = target.chain[j]&0x3F;
                                if(type1==Assignment::Target::SWIZZLE || type2==Assignment::Target::SWIZZLE)
                                {
-                                       unsigned index1 = a->target.chain[j]&0x3F;
-                                       unsigned index2 = target.chain[j]&0x3F;
                                        if(type1==Assignment::Target::SWIZZLE && type2==Assignment::Target::SWIZZLE)
                                                covered = index1&index2;
                                        else if(type1==Assignment::Target::ARRAY && index1<4)
@@ -1109,7 +1304,7 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n
                                        covered as true */
                                }
                                else
-                                       covered = (a->target.chain[j]==target.chain[j]);
+                                       covered = (type1==type2 && (index1==index2 || index1==0x3F || index2==0x3F));
                        }
 
                        if(covered)
@@ -1290,14 +1485,14 @@ void UnusedVariableRemover::visit(VariableDeclaration &var)
                return;
 
        VariableInfo &var_info = variables[&var];
-       var_info.interface_block = interface_block;
 
        /* Mark variables as output if they're used by the next stage or the
        graphics API. */
-       if(interface_block)
-               var_info.output = (interface_block->interface=="out" && (interface_block->linked_block || !interface_block->block_name.compare(0, 3, "gl_")));
-       else
-               var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
+       var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
+
+       // Linked outputs are automatically referenced.
+       if(var_info.output && var.linked_declaration)
+               var_info.referenced = true;
 
        if(var.init_expression)
        {