From 24e4eea282917c3f6e4e16a2825372da64133e35 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 9 Oct 2021 19:44:06 +0300 Subject: [PATCH] Break up struct variables if they're not used as a whole This lets the optimizer inline or discard individual members depending on how or if they're used. --- source/glsl/compiler.cpp | 5 ++ source/glsl/optimize.cpp | 147 +++++++++++++++++++++++++++++++++++++++ source/glsl/optimize.h | 45 ++++++++++++ 3 files changed, 197 insertions(+) diff --git a/source/glsl/compiler.cpp b/source/glsl/compiler.cpp index 506fba1c..4b1d4edf 100644 --- a/source/glsl/compiler.cpp +++ b/source/glsl/compiler.cpp @@ -363,6 +363,11 @@ Compiler::OptimizeResult Compiler::optimize(Stage &stage) resolve(stage, RESOLVE_TYPES|RESOLVE_VARIABLES|RESOLVE_FUNCTIONS|RESOLVE_EXPRESSIONS); any_inlined = true; } + if(AggregateDismantler().apply(stage)) + { + resolve(stage, RESOLVE_TYPES|RESOLVE_VARIABLES|RESOLVE_FUNCTIONS|RESOLVE_EXPRESSIONS); + any_inlined = true; + } if(ExpressionInliner().apply(stage)) { resolve(stage, RESOLVE_VARIABLES|RESOLVE_FUNCTIONS|RESOLVE_EXPRESSIONS); diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 8c586e7a..350e2eb7 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -520,6 +520,153 @@ 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) + { + VariableDeclaration *var = new VariableDeclaration; + var->source = kvp.first->source; + var->line = kvp.first->line; + var->name = get_unused_variable_name(*kvp.second.decl_scope, format("%s_%s", kvp.second.declaration->name, m.declaration->name)); + var->type = m.declaration->type; + if(m.initializer) + var->init_expression = m.initializer->clone(); + + kvp.second.decl_scope->body.insert(kvp.second.insert_point, var); + + for(RefPtr *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 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 &expr) +{ + r_aggregate_ref = 0; + expr->visit(*this); + if(r_aggregate_ref && r_reference.chain_len==1 && (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; + } + r_aggregate_ref = 0; +} + +void AggregateDismantler::visit(VariableReference &var) +{ + if(composite_reference) + r_reference.declaration = var.declaration; + else + { + auto i = aggregates.find(var.declaration); + if(i!=aggregates.end()) + i->second.referenced = true; + } +} + +void AggregateDismantler::visit_composite(RefPtr &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); + } + + add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F); + } + 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(var.type_declaration)) + { + const FunctionCall *init_call = dynamic_cast(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 &s: strct->members.body) + { + if(const VariableDeclaration *mem_decl = dynamic_cast(s.get())) + { + AggregateMember member; + member.declaration = mem_decl; + if(init_call) + member.initializer = init_call->arguments[i]; + aggre.members.push_back(member); + } + ++i; + } + } + } +} + + template T ConstantFolder::evaluate_logical(char oper, T left, T right) { diff --git a/source/glsl/optimize.h b/source/glsl/optimize.h index ae55e5af..1eb0464e 100644 --- a/source/glsl/optimize.h +++ b/source/glsl/optimize.h @@ -151,6 +151,51 @@ private: virtual void visit(Iteration &); }; +/** +Breaks aggregates up into separate variables if only the individual fields are +accessed and not the aggregate as a whole. +*/ +class AggregateDismantler: public TraversingVisitor +{ +private: + struct AggregateMember + { + const VariableDeclaration *declaration = 0; + RefPtr initializer; + std::vector *> references; + }; + + struct Aggregate + { + VariableDeclaration *declaration = 0; + Block *decl_scope = 0; + NodeList::iterator insert_point; + std::vector members; + bool referenced = false; + bool members_referenced = false; + }; + + NodeList::iterator insert_point; + std::map aggregates; + bool composite_reference = false; + Assignment::Target r_reference; + Aggregate *r_aggregate_ref = 0; + +public: + bool apply(Stage &); + +private: + virtual void visit(Block &); + virtual void visit(RefPtr &); + virtual void visit(VariableReference &); + void visit_composite(RefPtr &); + virtual void visit(MemberAccess &); + virtual void visit(BinaryExpression &); + virtual void visit(StructDeclaration &) { } + virtual void visit(VariableDeclaration &); + virtual void visit(InterfaceBlock &) { } +}; + /** Replaces expressions consisting entirely of literals with the results of evaluating the expression.*/ class ConstantFolder: private TraversingVisitor -- 2.43.0