}
+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)
{
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);
{
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)
covered as true */
}
else
- covered = (a->target.chain[j]==target.chain[j]);
+ covered = (type1==type2 && (index1==index2 || index1==0x3F || index2==0x3F));
}
if(covered)
{
if(composite_reference)
r_reference.declaration = var.declaration;
- else
+ else if(var.declaration)
referenced(var.declaration, var);
}
{
if(composite_reference)
r_reference.declaration = iface.declaration;
- else
+ else if(iface.declaration)
referenced(iface.declaration, iface);
}
{
SetFlag clear_assignment(assignment_target, false);
SetFlag clear_composite(composite_reference, false);
+ SetForScope<Assignment::Target> clear_reference(r_reference, Assignment::Target());
binary.right->visit(*this);
}
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)
{