X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fglsl%2Foptimize.cpp;h=6e4b56771fa3222ce778a53d37b828b5d34554cf;hb=1a5dafe20e47c764f2914c341fb7b8f1fba59fb8;hp=8c586e7ad886cf433c4c5cadf6315783980804a6;hpb=38712d8ecc57d043a2419ffbaeeb57f7a6586f14;p=libs%2Fgl.git diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 8c586e7a..6e4b5677 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -114,9 +114,17 @@ string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_fu source_func = call.declaration->definition; /* Populate referenced_names from the target function so we can rename - variables from the inlined function that would conflict. */ + variables from the inlined function that would conflict. Only consider + names declared in blocks linearly related to the target block. */ pass = REFERENCED; - target_func.visit(*this); + tgt_blk.visit(*this); + for(const Block *b=&tgt_blk; b; b=b->parent) + for(const auto &kvp: b->variables) + referenced_names.insert(kvp.first); + for(const auto &kvp: stage.interface_blocks) + if(kvp.second->name.find(' ')!=string::npos) + for(const auto &kvp2: kvp.second->block_declaration->members.variables) + referenced_names.insert(kvp2.first); /* Inline and rename passes must be interleaved so used variable names are known when inlining the return statement. */ @@ -187,12 +195,6 @@ void InlineContentInjector::visit(VariableReference &var) referenced_names.insert(var.name); } -void InlineContentInjector::visit(InterfaceBlockReference &iface) -{ - if(pass==REFERENCED) - referenced_names.insert(iface.name); -} - void InlineContentInjector::visit(FunctionCall &call) { if(pass==REFERENCED) @@ -356,7 +358,7 @@ void ExpressionInliner::visit(RefPtr &expr) ExpressionUse use; use.reference = &expr; use.ref_scope = current_block; - use.blocked = access_write; + use.blocked = access_write || r_ref_info->blocked; if(iteration_body && !r_ref_info->trivial) { @@ -402,7 +404,7 @@ void ExpressionInliner::visit(Swizzle &swizzle) void ExpressionInliner::visit(UnaryExpression &unary) { - SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-'); + SetFlag set_write(access_write, (unary.oper->token[1]=='+' || unary.oper->token[1]=='-')); visit(unary.expression); r_trivial = false; } @@ -428,10 +430,10 @@ void ExpressionInliner::visit(Assignment &assign) r_trivial = true; visit(assign.right); - auto i = assignments.find(assign.target); + auto i = assignments.find(assign.target.declaration); if(i!=assignments.end()) { - if(iteration_body && i->second->expression) + if(iteration_body && i->second && i->second->expression) { /* Block inlining into previous references within the iteration statement. On iterations after the first they would refer to the @@ -441,7 +443,11 @@ void ExpressionInliner::visit(Assignment &assign) u.blocked = (k==iteration_body); } - expressions.push_back(ExpressionInfo()); + for(; (i!=assignments.end() && i->first.declaration==assign.target.declaration); ++i) + if(targets_overlap(i->first, assign.target)) + i->second->blocked = true; + + expressions.emplace_back(); ExpressionInfo &info = expressions.back(); info.target = assign.target; // Self-referencing assignments can't be inlined without additional work. @@ -450,7 +456,7 @@ void ExpressionInliner::visit(Assignment &assign) info.assign_scope = current_block; info.trivial = r_trivial; - i->second = &info; + assignments[assign.target] = &info; } r_trivial = false; @@ -488,7 +494,7 @@ void ExpressionInliner::visit(VariableDeclaration &var) analyze and non-trivial expressions could be expensive to inline. */ if((current_block->parent || (constant && r_trivial)) && var.interface.empty()) { - expressions.push_back(ExpressionInfo()); + expressions.emplace_back(); ExpressionInfo &info = expressions.back(); info.target = &var; /* Assume variables declared in an iteration initialization statement @@ -520,6 +526,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 *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) + { + 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 &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(binary.right.get())) + if(literal_subscript->value.check_type()) + index = literal_subscript->value.value(); + 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(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; + 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(var.array_size.get())) + { + if(literal_size->value.check_type()) + { + Aggregate &aggre = aggregates[&var]; + aggre.declaration = &var; + aggre.decl_scope = current_block; + aggre.insert_point = insert_point; + + int size = literal_size->value.value(); + for(int i=0; i T ConstantFolder::evaluate_logical(char oper, T left, T right) { @@ -846,10 +1055,11 @@ void ConstantFolder::visit(Iteration &iter) } -void ConstantConditionEliminator::apply(Stage &stage) +bool ConstantConditionEliminator::apply(Stage &stage) { stage.content.visit(*this); NodeRemover().apply(stage, nodes_to_remove); + return !nodes_to_remove.empty(); } ConstantConditionEliminator::ConstantStatus ConstantConditionEliminator::check_constant_condition(const Expression &expr) @@ -1034,11 +1244,6 @@ void UnusedTypeRemover::visit(VariableDeclaration &var) TraversingVisitor::visit(var); } -void UnusedTypeRemover::visit(InterfaceBlock &iface) -{ - unused_nodes.erase(iface.type_declaration); -} - void UnusedTypeRemover::visit(FunctionDeclaration &func) { unused_nodes.erase(func.return_type_declaration); @@ -1057,23 +1262,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); @@ -1089,36 +1286,12 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n { bool loop_external = false; for(AssignmentInfo *a: var_info.assignments) - { - bool covered = true; - for(unsigned j=0; (covered && jtarget.chain_len && j(a->target.chain[j]&0xC0); - Assignment::Target::ChainType type2 = static_cast(target.chain[j]&0xC0); - 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 = index2&(1<target.chain[j]==target.chain[j]); - } - - if(covered) + if(targets_overlap(a->target, target)) { a->used_by.push_back(&node); if(a->in_loop clear_reference(r_reference, Assignment::Target()); binary.right->visit(*this); } @@ -1239,7 +1405,7 @@ void UnusedVariableRemover::visit(FunctionCall &call) void UnusedVariableRemover::record_assignment(const Assignment::Target &target, Node &node) { - assignments.push_back(AssignmentInfo()); + assignments.emplace_back(); AssignmentInfo &assign_info = assignments.back(); assign_info.node = &node; assign_info.target = target; @@ -1290,14 +1456,15 @@ 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_"))); + bool builtin = (!var.name.compare(0, 3, "gl_") || (var.block_declaration && !var.block_declaration->block_name.compare(0, 3, "gl_"))); + var_info.output = (var.interface=="out" && (stage->type==Stage::FRAGMENT || var.linked_declaration || builtin)); + + // Linked outputs are automatically referenced. + if(var_info.output && var.linked_declaration) + var_info.referenced = true; if(var.init_expression) { @@ -1306,12 +1473,6 @@ void UnusedVariableRemover::visit(VariableDeclaration &var) } } -void UnusedVariableRemover::visit(InterfaceBlock &iface) -{ - VariableInfo &var_info = variables[&iface]; - var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.block_name.compare(0, 3, "gl_"))); -} - void UnusedVariableRemover::merge_variables(const BlockVariableMap &other_vars) { for(const auto &kvp: other_vars) @@ -1378,8 +1539,14 @@ void UnusedVariableRemover::visit(Iteration &iter) vector saved_refs; swap(loop_ext_refs, saved_refs); { + if(iter.init_statement) + iter.init_statement->visit(*this); SetForScope set_loop(in_loop, in_loop+1); - TraversingVisitor::visit(iter); + if(iter.condition) + iter.condition->visit(*this); + iter.body.visit(*this); + if(iter.loop_expression) + iter.loop_expression->visit(*this); } swap(loop_ext_refs, saved_refs);