X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fglsl%2Foptimize.cpp;h=0f765c2a4aa6ce23edd99f2dd0fed35e4041bd3c;hb=305b62cf4f7e2a4ca3cc56109003aed6bde61c25;hp=6c49878b857603152d742383ab4fd1f611e6c1ac;hpb=499c0515cde44b304d131ac7ac3b2030c9dbe11c;p=libs%2Fgl.git diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 6c49878b..0f765c2a 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -1,6 +1,7 @@ #include #include #include "optimize.h" +#include "reflect.h" using namespace std; @@ -8,6 +9,55 @@ namespace Msp { namespace GL { namespace SL { +ConstantSpecializer::ConstantSpecializer(): + values(0) +{ } + +void ConstantSpecializer::apply(Stage &stage, const map &v) +{ + values = &v; + stage.content.visit(*this); +} + +void ConstantSpecializer::visit(VariableDeclaration &var) +{ + bool specializable = false; + if(var.layout) + { + vector &qualifiers = var.layout->qualifiers; + for(vector::iterator i=qualifiers.begin(); (!specializable && i!=qualifiers.end()); ++i) + if(i->name=="constant_id") + { + specializable = true; + qualifiers.erase(i); + } + + if(qualifiers.empty()) + var.layout = 0; + } + + if(specializable) + { + map::const_iterator i = values->find(var.name); + if(i!=values->end()) + { + RefPtr literal = new Literal; + if(var.type=="bool") + { + literal->token = (i->second ? "true" : "false"); + literal->value = static_cast(i->second); + } + else if(var.type=="int") + { + literal->token = lexical_cast(i->second); + literal->value = i->second; + } + var.init_expression = literal; + } + } +} + + InlineableFunctionLocator::InlineableFunctionLocator(): current_function(0), return_count(0) @@ -25,7 +75,7 @@ void InlineableFunctionLocator::visit(FunctionCall &call) ++count; /* Don't inline functions which are called more than once or are called recursively. */ - if(count>1 || def==current_function) + if((count>1 && def->source!=BUILTIN_SOURCE) || def==current_function) inlineable.erase(def); } @@ -34,8 +84,12 @@ void InlineableFunctionLocator::visit(FunctionCall &call) void InlineableFunctionLocator::visit(FunctionDeclaration &func) { + bool has_out_params = false; + for(NodeArray::const_iterator i=func.parameters.begin(); (!has_out_params && i!=func.parameters.end()); ++i) + has_out_params = ((*i)->interface=="out"); + unsigned &count = refcounts[func.definition]; - if(count<=1 && func.parameters.empty()) + if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params) inlineable.insert(func.definition); SetForScope set(current_function, &func); @@ -66,80 +120,97 @@ void InlineableFunctionLocator::visit(Return &ret) InlineContentInjector::InlineContentInjector(): source_func(0), - remap_names(false), - deps_only(false) + pass(REFERENCED) { } -const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionDeclaration &src) +string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionCall &call) { - target_block = &tgt_blk; - source_func = &src; - remap_prefix = source_func->name; + source_func = call.declaration->definition; - vector > inlined; - inlined.reserve(src.body.body.size()); - for(NodeList::iterator i=src.body.body.begin(); i!=src.body.body.end(); ++i) + /* Populate referenced_names from the target function so we can rename + variables from the inlined function that would conflict. */ + pass = REFERENCED; + target_func.visit(*this); + + /* Inline and rename passes must be interleaved so used variable names are + known when inlining the return statement. */ + pass = INLINE; + staging_block.parent = &tgt_blk; + staging_block.variables.clear(); + + std::vector > params; + params.reserve(source_func->parameters.size()); + for(NodeArray::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i) + { + RefPtr var = (*i)->clone(); + var->interface.clear(); + + SetForScope set_pass(pass, RENAME); + var->visit(*this); + + staging_block.body.push_back_nocopy(var); + params.push_back(var); + } + + for(NodeList::iterator i=source_func->body.body.begin(); i!=source_func->body.body.end(); ++i) { r_inlined_statement = 0; (*i)->visit(*this); if(!r_inlined_statement) r_inlined_statement = (*i)->clone(); - SetForScope set_remap(remap_names, 2); + SetForScope set_pass(pass, RENAME); r_inlined_statement->visit(*this); - inlined.push_back(r_inlined_statement); + + staging_block.body.push_back_nocopy(r_inlined_statement); } - SetForScope set_remap(remap_names, 1); - SetForScope set_prefix(remap_prefix, target_func.name); - variable_map.clear(); + /* Now collect names from the staging block. Local variables that would + have conflicted with the target function were renamed earlier. */ + pass = REFERENCED; + referenced_names.clear(); + staging_block.variables.clear(); + staging_block.visit(*this); + + /* Rename variables in the target function so they don't interfere with + global identifiers used by the source function. */ + pass = RENAME; + staging_block.parent = source_func->body.parent; target_func.visit(*this); - tgt_blk.body.insert(ins_pt, inlined.begin(), inlined.end()); + // Put the argument expressions in place after all renaming has been done. + for(unsigned i=0; iparameters.size(); ++i) + params[i]->init_expression = call.arguments[i]->clone(); - NodeReorderer().apply(stage, target_func, dependencies); + tgt_blk.body.splice(ins_pt, staging_block.body); + + NodeReorderer().apply(stage, target_func, DependencyCollector().apply(*source_func)); return r_result_name; } void InlineContentInjector::visit(VariableReference &var) { - if(remap_names) + if(pass==RENAME) { - map::const_iterator i = variable_map.find(var.name); - if(i!=variable_map.end()) + map::const_iterator i = staging_block.variables.find(var.name); + if(i!=staging_block.variables.end()) var.name = i->second->name; } - else if(var.declaration) - { - SetFlag set_deps(deps_only); - if(!variable_map.count(var.name)) - { - dependencies.insert(var.declaration); - referenced_names.insert(var.name); - } - var.declaration->visit(*this); - } + else if(pass==REFERENCED) + referenced_names.insert(var.name); } void InlineContentInjector::visit(InterfaceBlockReference &iface) { - if(!remap_names && iface.declaration) - { - SetFlag set_deps(deps_only); - dependencies.insert(iface.declaration); + if(pass==REFERENCED) referenced_names.insert(iface.name); - iface.declaration->visit(*this); - } } void InlineContentInjector::visit(FunctionCall &call) { - if(!remap_names && call.declaration) - { - dependencies.insert(call.declaration); + if(pass==REFERENCED) referenced_names.insert(call.name); - } TraversingVisitor::visit(call); } @@ -147,33 +218,34 @@ void InlineContentInjector::visit(VariableDeclaration &var) { TraversingVisitor::visit(var); - if(remap_names) + if(pass==RENAME) { - if(remap_names==2 || referenced_names.count(var.name)) + /* Check against conflicts with the other context as well as variables + already renamed here. */ + bool conflict = (staging_block.variables.count(var.name) || referenced_names.count(var.name)); + staging_block.variables[var.name] = &var; + if(conflict) { - string mapped_name = get_unused_variable_name(*target_block, var.name, remap_prefix); - variable_map[var.name] = &var; - var.name = mapped_name; + string mapped_name = get_unused_variable_name(staging_block, var.name); + if(mapped_name!=var.name) + { + staging_block.variables[mapped_name] = &var; + var.name = mapped_name; + } } } - else if(var.type_declaration) - { - SetFlag set_deps(deps_only); - dependencies.insert(var.type_declaration); - referenced_names.insert(var.type_declaration->name); - var.type_declaration->visit(*this); - } + else if(pass==REFERENCED) + referenced_names.insert(var.type); } void InlineContentInjector::visit(Return &ret) { TraversingVisitor::visit(ret); - if(!remap_names && ret.expression) + if(pass==INLINE && ret.expression) { - /* Create a new variable to hold the return value of the inlined - function. */ - r_result_name = get_unused_variable_name(*target_block, "_return", source_func->name); + // Create a new variable to hold the return value of the inlined function. + r_result_name = get_unused_variable_name(staging_block, "_return"); RefPtr var = new VariableDeclaration; var->source = ret.source; var->line = ret.line; @@ -225,23 +297,23 @@ void FunctionInliner::visit(Block &block) void FunctionInliner::visit(FunctionCall &call) { + for(NodeArray::iterator i=call.arguments.begin(); (!r_inlined_here && i!=call.arguments.end()); ++i) + visit(*i); + if(r_inlined_here) return; - for(NodeArray::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i) - visit(*i); - FunctionDeclaration *def = call.declaration; if(def) def = def->definition; if(def && inlineable.count(def)) { - string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, *def); + string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, call); // This will later get removed by UnusedVariableRemover. if(result_name.empty()) - result_name = "msp_unused_from_inline"; + result_name = "_msp_unused_from_inline"; RefPtr ref = new VariableReference; ref->name = result_name; @@ -276,15 +348,6 @@ void FunctionInliner::visit(Iteration &iter) } -ExpressionInliner::ExpressionInfo::ExpressionInfo(): - expression(0), - assign_scope(0), - inline_point(0), - trivial(false), - available(true) -{ } - - ExpressionInliner::ExpressionInliner(): r_ref_info(0), r_any_inlined(false), @@ -851,33 +914,10 @@ bool UnusedTypeRemover::apply(Stage &stage) return !unused_nodes.empty(); } -void UnusedTypeRemover::visit(Literal &literal) -{ - unused_nodes.erase(literal.type); -} - -void UnusedTypeRemover::visit(UnaryExpression &unary) +void UnusedTypeRemover::visit(RefPtr &expr) { - unused_nodes.erase(unary.type); - TraversingVisitor::visit(unary); -} - -void UnusedTypeRemover::visit(BinaryExpression &binary) -{ - unused_nodes.erase(binary.type); - TraversingVisitor::visit(binary); -} - -void UnusedTypeRemover::visit(TernaryExpression &ternary) -{ - unused_nodes.erase(ternary.type); - TraversingVisitor::visit(ternary); -} - -void UnusedTypeRemover::visit(FunctionCall &call) -{ - unused_nodes.erase(call.type); - TraversingVisitor::visit(call); + unused_nodes.erase(expr->type); + TraversingVisitor::visit(expr); } void UnusedTypeRemover::visit(BasicTypeDeclaration &type) @@ -903,6 +943,7 @@ void UnusedTypeRemover::visit(StructDeclaration &strct) void UnusedTypeRemover::visit(VariableDeclaration &var) { unused_nodes.erase(var.type_declaration); + TraversingVisitor::visit(var); } void UnusedTypeRemover::visit(InterfaceBlock &iface) @@ -922,7 +963,8 @@ UnusedVariableRemover::UnusedVariableRemover(): interface_block(0), r_assignment(0), assignment_target(false), - r_side_effects(false) + r_side_effects(false), + composite_reference(false) { } bool UnusedVariableRemover::apply(Stage &s) @@ -934,10 +976,6 @@ bool UnusedVariableRemover::apply(Stage &s) if(i->used_by.empty()) unused_nodes.insert(i->node); - for(map::const_iterator i=s.interface_blocks.begin(); i!=s.interface_blocks.end(); ++i) - if(i->second->instance_name.empty()) - unused_nodes.insert(i->second); - for(BlockVariableMap::const_iterator i=variables.begin(); i!=variables.end(); ++i) { if(i->second.output) @@ -971,18 +1009,80 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n if(!assignment_target) { for(vector::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i) - (*i)->used_by.push_back(&node); + { + bool covered = true; + for(unsigned j=0; (covered && j<(*i)->target.chain_len && j((*i)->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 = (*i)->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) + (*i)->used_by.push_back(&node); + } } } void UnusedVariableRemover::visit(VariableReference &var) { - referenced(var.declaration, var); + if(composite_reference) + r_reference.declaration = var.declaration; + else + referenced(var.declaration, var); } void UnusedVariableRemover::visit(InterfaceBlockReference &iface) { - referenced(iface.declaration, iface); + if(composite_reference) + r_reference.declaration = iface.declaration; + else + referenced(iface.declaration, iface); +} + +void UnusedVariableRemover::visit_composite(Expression &expr) +{ + if(!composite_reference) + r_reference = Assignment::Target(); + + SetFlag set_composite(composite_reference); + expr.visit(*this); +} + +void UnusedVariableRemover::visit(MemberAccess &memacc) +{ + visit_composite(*memacc.left); + + add_to_chain(r_reference, Assignment::Target::MEMBER, memacc.index); + + if(!composite_reference && r_reference.declaration) + referenced(r_reference, memacc); +} + +void UnusedVariableRemover::visit(Swizzle &swizzle) +{ + visit_composite(*swizzle.left); + + unsigned mask = 0; + for(unsigned i=0; itoken[0]=='[') { - binary.left->visit(*this); - SetFlag set(assignment_target, false); - binary.right->visit(*this); + visit_composite(*binary.left); + + { + SetFlag clear_assignment(assignment_target, false); + SetFlag clear_composite(composite_reference, false); + binary.right->visit(*this); + } + + add_to_chain(r_reference, Assignment::Target::ARRAY, 0x3F); + + if(!composite_reference && r_reference.declaration) + referenced(r_reference, binary); } else + { + SetFlag clear_composite(composite_reference, false); TraversingVisitor::visit(binary); + } +} + +void UnusedVariableRemover::visit(TernaryExpression &ternary) +{ + SetFlag clear_composite(composite_reference, false); + TraversingVisitor::visit(ternary); } void UnusedVariableRemover::visit(Assignment &assign) @@ -1017,6 +1135,7 @@ void UnusedVariableRemover::visit(Assignment &assign) void UnusedVariableRemover::visit(FunctionCall &call) { + SetFlag clear_composite(composite_reference, false); TraversingVisitor::visit(call); /* Treat function calls as having side effects so expression statements consisting of nothing but a function call won't be optimized away. */ @@ -1076,7 +1195,7 @@ void UnusedVariableRemover::visit(VariableDeclaration &var) /* 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_"))); + 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_"))); @@ -1090,16 +1209,8 @@ void UnusedVariableRemover::visit(VariableDeclaration &var) void UnusedVariableRemover::visit(InterfaceBlock &iface) { - if(iface.instance_name.empty()) - { - SetForScope set_block(interface_block, &iface); - iface.struct_declaration->members.visit(*this); - } - else - { - VariableInfo &var_info = variables[&iface]; - var_info.output = (iface.interface=="out" && (iface.linked_block || !iface.name.compare(0, 3, "gl_"))); - } + 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)