+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);
+ }
+ }
+ }
+ }
+}
+
+