UnusedVariableRemover::VariableInfo::VariableInfo():
local(false),
+ output(false),
conditionally_assigned(false),
- referenced(false)
+ referenced(false),
+ interface_block(0)
{ }
UnusedVariableRemover::UnusedVariableRemover():
- aggregate(0),
+ stage(0),
+ interface_block(0),
r_assignment(0),
assignment_target(false),
- r_assign_to_subfield(false),
r_side_effects(false)
{ }
-bool UnusedVariableRemover::apply(Stage &stage)
+bool UnusedVariableRemover::apply(Stage &s)
{
+ stage = &s;
variables.push_back(BlockVariableMap());
- stage.content.visit(*this);
+ s.content.visit(*this);
+
BlockVariableMap &global_variables = variables.back();
+ set<InterfaceBlock *> used_interface_blocks;
+ Statement *prev_decl = 0;
+ bool output;
for(BlockVariableMap::iterator i=global_variables.begin(); i!=global_variables.end(); ++i)
{
- string interface = i->first->interface;
- bool linked = i->first->linked_declaration;
- map<VariableDeclaration *, Node *>::iterator j = aggregates.find(i->first);
- if(j!=aggregates.end())
- if(InterfaceBlock *iface = dynamic_cast<InterfaceBlock *>(j->second))
- {
- interface = iface->interface;
- linked = iface->linked_block;
- }
-
- /* Don't remove output variables which are used by the next stage or the
- graphics API. */
- if(interface=="out" && (stage.type==Stage::FRAGMENT || linked || !i->first->name.compare(0, 3, "gl_")))
+ if(i->first.declaration!=prev_decl)
+ {
+ prev_decl = i->first.declaration;
+ output = i->second.output;
+ }
+ if(output)
+ {
+ if(!i->second.assignments.empty() && i->second.interface_block)
+ used_interface_blocks.insert(i->second.interface_block);
continue;
+ }
// Mark other unreferenced global variables as unused.
if(!i->second.referenced)
{
- unused_nodes.insert(i->first);
+ if(!i->second.interface_block && !i->first.chain_len)
+ unused_nodes.insert(i->first.declaration);
clear_assignments(i->second, true);
}
+ else if(i->second.interface_block)
+ used_interface_blocks.insert(i->second.interface_block);
}
variables.pop_back();
- NodeRemover().apply(stage, unused_nodes);
+ for(map<string, InterfaceBlock *>::const_iterator i=s.interface_blocks.begin(); i!=s.interface_blocks.end(); ++i)
+ if(i->second->instance_name.empty() && !used_interface_blocks.count(i->second))
+ unused_nodes.insert(i->second);
+
+ NodeRemover().apply(s, unused_nodes);
return !unused_nodes.empty();
}
-void UnusedVariableRemover::visit(VariableReference &var)
+void UnusedVariableRemover::reference_used(Statement &declaration)
{
- map<VariableDeclaration *, Node *>::iterator i = aggregates.find(var.declaration);
- if(i!=aggregates.end())
- unused_nodes.erase(i->second);
-
- if(var.declaration && !assignment_target)
+ BlockVariableMap &block_vars = variables.back();
+ /* Previous assignments of all subfields of this variable are used by
+ this reference. */
+ for(BlockVariableMap::iterator i=block_vars.lower_bound(&declaration); (i!=block_vars.end() && i->first.declaration==&declaration); ++i)
{
- VariableInfo &var_info = variables.back()[var.declaration];
- // Previous assignments are used by this reference.
- clear_assignments(var_info, false);
- var_info.referenced = true;
+ clear_assignments(i->second, false);
+ i->second.referenced = true;
}
-}
-void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
-{
- unused_nodes.erase(iface.declaration);
+ // Always record a reference to the primary declaration, even if it didn't exist before
+ block_vars[&declaration].referenced = true;
}
-void UnusedVariableRemover::visit(MemberAccess &memacc)
+void UnusedVariableRemover::visit(VariableReference &var)
{
- if(assignment_target)
- r_assign_to_subfield = true;
- TraversingVisitor::visit(memacc);
- unused_nodes.erase(memacc.declaration);
+ if(var.declaration && !assignment_target)
+ reference_used(*var.declaration);
}
-void UnusedVariableRemover::visit(Swizzle &swizzle)
+void UnusedVariableRemover::visit(InterfaceBlockReference &iface)
{
- if(assignment_target)
- r_assign_to_subfield = true;
- TraversingVisitor::visit(swizzle);
+ if(iface.declaration && !assignment_target)
+ reference_used(*iface.declaration);
}
void UnusedVariableRemover::visit(UnaryExpression &unary)
{
if(binary.oper->token[0]=='[')
{
- if(assignment_target)
- r_assign_to_subfield = true;
binary.left->visit(*this);
SetFlag set(assignment_target, false);
binary.right->visit(*this);
r_side_effects = true;
}
-void UnusedVariableRemover::record_assignment(VariableDeclaration &var, Node &node, bool chained)
+void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node, bool chained)
{
- VariableInfo &var_info = variables.back()[&var];
- /* An assignment which completely replaces the value of the variable causes
- any previous unreferenced assignments to be unused. */
- if(!chained)
- clear_assignments(var_info, true);
+ BlockVariableMap &block_vars = variables.back();
+ for(BlockVariableMap::iterator i=block_vars.lower_bound(target); (i!=block_vars.end() && i->first.declaration==target.declaration); ++i)
+ {
+ bool subfield = (i->first.chain_len>=target.chain_len);
+ for(unsigned j=0; (subfield && j<target.chain_len); ++j)
+ subfield = (i->first.chain[j]==target.chain[j]);
+ if(!subfield)
+ break;
+
+ /* An assignment to the target causes any previous unreferenced
+ assignments to the same target or its subfields to be unused. */
+ if(!chained)
+ clear_assignments(i->second, true);
+ }
+
+ VariableInfo &var_info = variables.back()[target];
var_info.assignments.push_back(&node);
var_info.conditionally_assigned = false;
}
void UnusedVariableRemover::visit(ExpressionStatement &expr)
{
r_assignment = 0;
- r_assign_to_subfield = false;
r_side_effects = false;
TraversingVisitor::visit(expr);
if(r_assignment && r_assignment->target.declaration)
- if(VariableDeclaration *target_var = dynamic_cast<VariableDeclaration *>(r_assignment->target.declaration))
- record_assignment(*target_var, expr, (r_assignment->self_referencing || r_assign_to_subfield));
+ record_assignment(r_assignment->target, expr, r_assignment->self_referencing);
if(!r_side_effects)
unused_nodes.insert(&expr);
}
-void UnusedVariableRemover::visit(StructDeclaration &strct)
-{
- SetForScope<Node *> set(aggregate, &strct);
- TraversingVisitor::visit(strct);
-}
-
void UnusedVariableRemover::visit(VariableDeclaration &var)
{
- if(aggregate)
- aggregates[&var] = aggregate;
+ VariableInfo &var_info = variables.back()[&var];
+ var_info.local = true;
+ 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->name.compare(0, 3, "gl_")));
else
- {
- variables.back()[&var].local = true;
- if(var.init_expression)
- record_assignment(var, *var.init_expression, false);
- }
+ var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || !var.name.compare(0, 3, "gl_")));
+
+ if(var.init_expression)
+ record_assignment(&var, *var.init_expression, false);
TraversingVisitor::visit(var);
}
void UnusedVariableRemover::visit(InterfaceBlock &iface)
{
- SetForScope<Node *> set(aggregate, &iface);
- unused_nodes.insert(&iface);
- iface.struct_declaration->members.visit(*this);
+ if(iface.instance_name.empty())
+ {
+ SetForScope<InterfaceBlock *> set_block(interface_block, &iface);
+ iface.struct_declaration->members.visit(*this);
+ }
+ else
+ {
+ VariableInfo &var_info = variables.back()[&iface];
+ var_info.local = true;
+ var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.name.compare(0, 3, "gl_")));
+ }
}
void UnusedVariableRemover::visit(FunctionDeclaration &func)
{
if(i->second.local)
{
- if(!i->second.referenced)
- unused_nodes.insert(i->first);
+ if(!i->second.referenced && !i->first.chain_len)
+ unused_nodes.insert(i->first.declaration);
/* Any unreferenced assignments when a variable runs out of scope
become unused. */
clear_assignments(i->second, true);
private:
struct VariableInfo
{
- bool local;
std::vector<Node *> assignments;
+ bool local;
+ bool output;
bool conditionally_assigned;
bool referenced;
+ InterfaceBlock *interface_block;
VariableInfo();
};
- typedef std::map<VariableDeclaration *, VariableInfo> BlockVariableMap;
+ typedef std::map<Assignment::Target, VariableInfo> BlockVariableMap;
+ Stage *stage;
std::set<Node *> unused_nodes;
- std::map<VariableDeclaration *, Node *> aggregates;
- Node *aggregate;
std::vector<BlockVariableMap> variables;
+ InterfaceBlock *interface_block;
Assignment *r_assignment;
bool assignment_target;
- bool r_assign_to_subfield;
bool r_side_effects;
public:
bool apply(Stage &);
private:
+ void reference_used(Statement &);
virtual void visit(VariableReference &);
virtual void visit(InterfaceBlockReference &);
- virtual void visit(MemberAccess &);
- virtual void visit(Swizzle &);
virtual void visit(UnaryExpression &);
virtual void visit(BinaryExpression &);
virtual void visit(Assignment &);
- void record_assignment(VariableDeclaration &, Node &, bool);
+ void record_assignment(const Assignment::Target &, Node &, bool);
void clear_assignments(VariableInfo &, bool);
virtual void visit(FunctionCall &);
virtual void visit(ExpressionStatement &);
- virtual void visit(StructDeclaration &);
+ // Ignore structs because their members can't be accessed directly.
+ virtual void visit(StructDeclaration &) { }
virtual void visit(VariableDeclaration &);
virtual void visit(InterfaceBlock &);
virtual void visit(FunctionDeclaration &);