X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Fglsl%2Foptimize.cpp;h=ed20bb175dcc9277e1ff84e2e1a1734b2279668f;hp=668f7c5ef2553d399feef9c6c60949990fa54150;hb=8bf70f4d445cf63386ad7aafc5b3ebdbd8369939;hpb=d5c38c181cac4da490c96123e5a88a8c06785e55 diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 668f7c5e..ed20bb17 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -1,6 +1,8 @@ #include #include +#include #include "optimize.h" +#include "reflect.h" using namespace std; @@ -8,6 +10,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 +76,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 +85,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,75 +121,97 @@ void InlineableFunctionLocator::visit(Return &ret) InlineContentInjector::InlineContentInjector(): source_func(0), - remap_names(0) + 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) { + source_func = call.declaration->definition; + + /* 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; - source_func = &src; - remap_prefix = source_func->name; + staging_block.variables.clear(); + + 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=src.body.body.begin(); i!=src.body.body.end(); ++i) + 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); - staging_block.body.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); + /* 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); + // 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(); + tgt_blk.body.splice(ins_pt, staging_block.body); - NodeReorderer().apply(stage, target_func, dependencies); + 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 = staging_block.variables.find(var.name); if(i!=staging_block.variables.end()) var.name = i->second->name; } - else if(var.declaration) - { - if(!staging_block.variables.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) - { - 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); } @@ -142,34 +219,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(staging_block, var.name, remap_prefix); - staging_block.variables[var.name] = &var; + 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; + var.name = mapped_name; + } } } - else if(var.type_declaration) - { - 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(staging_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; @@ -221,23 +298,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; @@ -272,15 +349,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), @@ -486,18 +554,6 @@ void ExpressionInliner::visit(Iteration &iter) } -BasicTypeDeclaration::Kind ConstantFolder::get_value_kind(const Variant &value) -{ - if(value.check_type()) - return BasicTypeDeclaration::BOOL; - else if(value.check_type()) - return BasicTypeDeclaration::INT; - else if(value.check_type()) - return BasicTypeDeclaration::FLOAT; - else - return BasicTypeDeclaration::VOID; -} - template T ConstantFolder::evaluate_logical(char oper, T left, T right) { @@ -536,6 +592,31 @@ T ConstantFolder::evaluate_arithmetic(char oper, T left, T right) } } +template +T ConstantFolder::evaluate_int_special_op(char oper, T left, T right) +{ + switch(oper) + { + case '%': return left%right; + case '<': return left<': return left>>right; + default: return T(); + } +} + +template +void ConstantFolder::convert_to_result(const Variant &value) +{ + if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); + else if(value.check_type()) + set_result(static_cast(value.value())); +} + void ConstantFolder::set_result(const Variant &value, bool literal) { r_constant_value = value; @@ -556,22 +637,27 @@ void ConstantFolder::visit(RefPtr &expr) if(!r_constant || r_literal || r_uses_iter_var) return; - BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value); - if(kind==BasicTypeDeclaration::VOID) - { - r_constant = false; - return; - } - RefPtr literal = new Literal; - if(kind==BasicTypeDeclaration::BOOL) + if(r_constant_value.check_type()) literal->token = (r_constant_value.value() ? "true" : "false"); - else if(kind==BasicTypeDeclaration::INT) + else if(r_constant_value.check_type()) literal->token = lexical_cast(r_constant_value.value()); - else if(kind==BasicTypeDeclaration::FLOAT) + else if(r_constant_value.check_type()) + literal->token = lexical_cast(r_constant_value.value())+"u"; + else if(r_constant_value.check_type()) + { literal->token = lexical_cast(r_constant_value.value()); + if(isnumrc(literal->token)) + literal->token += ".0"; + } + else + { + r_constant = false; + return; + } literal->value = r_constant_value; expr = literal; + r_any_folded = true; } void ConstantFolder::visit(Literal &literal) @@ -611,25 +697,27 @@ void ConstantFolder::visit(UnaryExpression &unary) if(!can_fold) return; - BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value); - char oper = unary.oper->token[0]; char oper2 = unary.oper->token[1]; if(oper=='!') { - if(kind==BasicTypeDeclaration::BOOL) + if(r_constant_value.check_type()) set_result(!r_constant_value.value()); } else if(oper=='~') { - if(kind==BasicTypeDeclaration::INT) + if(r_constant_value.check_type()) set_result(~r_constant_value.value()); + else if(r_constant_value.check_type()) + set_result(~r_constant_value.value()); } else if(oper=='-' && !oper2) { - if(kind==BasicTypeDeclaration::INT) + if(r_constant_value.check_type()) set_result(-r_constant_value.value()); - else if(kind==BasicTypeDeclaration::FLOAT) + else if(r_constant_value.check_type()) + set_result(-r_constant_value.value()); + else if(r_constant_value.check_type()) set_result(-r_constant_value.value()); } } @@ -649,53 +737,54 @@ void ConstantFolder::visit(BinaryExpression &binary) if(!can_fold) return; - BasicTypeDeclaration::Kind left_kind = get_value_kind(left_value); - BasicTypeDeclaration::Kind right_kind = get_value_kind(r_constant_value); // Currently only expressions with both sides of equal types are handled. - if(left_kind!=right_kind) + if(!left_value.check_same_type(r_constant_value)) return; char oper = binary.oper->token[0]; char oper2 = binary.oper->token[1]; if(oper=='&' || oper=='|' || oper=='^') { - if(oper2==oper && left_kind==BasicTypeDeclaration::BOOL) + if(oper2==oper && left_value.check_type()) set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); - else if(!oper2 && left_kind==BasicTypeDeclaration::INT) + else if(!oper2 && left_value.check_type()) set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); + else if(!oper2 && left_value.check_type()) + set_result(evaluate_logical(oper, left_value.value(), r_constant_value.value())); } else if((oper=='<' || oper=='>') && oper2!=oper) { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); - else if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) set_result(evaluate_relation(binary.oper->token, left_value.value(), r_constant_value.value())); } else if((oper=='=' || oper=='!') && oper2=='=') { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); - if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); + else if(left_value.check_type()) set_result((left_value.value()==r_constant_value.value()) == (oper=='=')); } else if(oper=='+' || oper=='-' || oper=='*' || oper=='/') { - if(left_kind==BasicTypeDeclaration::INT) + if(left_value.check_type()) set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); - else if(left_kind==BasicTypeDeclaration::FLOAT) + else if(left_value.check_type()) + set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) set_result(evaluate_arithmetic(oper, left_value.value(), r_constant_value.value())); } else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper)) { - if(left_kind!=BasicTypeDeclaration::INT) - return; - - if(oper=='%') - set_result(left_value.value()%r_constant_value.value()); - else if(oper=='<') - set_result(left_value.value()<()); - else if(oper=='>') - set_result(left_value.value()>>r_constant_value.value()); + if(left_value.check_type()) + set_result(evaluate_int_special_op(oper, left_value.value(), r_constant_value.value())); + else if(left_value.check_type()) + set_result(evaluate_int_special_op(oper, left_value.value(), r_constant_value.value())); } } @@ -713,6 +802,30 @@ void ConstantFolder::visit(TernaryExpression &ternary) void ConstantFolder::visit(FunctionCall &call) { + if(call.constructor && call.type && call.arguments.size()==1) + { + const BasicTypeDeclaration *basic = dynamic_cast(call.type); + if(basic) + { + call.arguments[0]->visit(*this); + bool can_fold = r_constant; + r_constant = false; + if(!can_fold) + return; + + if(basic->kind==BasicTypeDeclaration::BOOL) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign) + convert_to_result(r_constant_value); + else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32) + convert_to_result(r_constant_value); + + return; + } + } + TraversingVisitor::visit(call); r_constant = false; } @@ -840,40 +953,63 @@ void ConstantConditionEliminator::visit(Iteration &iter) } -bool UnusedTypeRemover::apply(Stage &stage) +UnreachableCodeRemover::UnreachableCodeRemover(): + reachable(true) +{ } + +bool UnreachableCodeRemover::apply(Stage &stage) { stage.content.visit(*this); - NodeRemover().apply(stage, unused_nodes); - return !unused_nodes.empty(); + NodeRemover().apply(stage, unreachable_nodes); + return !unreachable_nodes.empty(); } -void UnusedTypeRemover::visit(Literal &literal) +void UnreachableCodeRemover::visit(Block &block) { - unused_nodes.erase(literal.type); + NodeList::iterator i = block.body.begin(); + for(; (reachable && i!=block.body.end()); ++i) + (*i)->visit(*this); + for(; i!=block.body.end(); ++i) + unreachable_nodes.insert(i->get()); } -void UnusedTypeRemover::visit(UnaryExpression &unary) +void UnreachableCodeRemover::visit(FunctionDeclaration &func) { - unused_nodes.erase(unary.type); - TraversingVisitor::visit(unary); + TraversingVisitor::visit(func); + reachable = true; } -void UnusedTypeRemover::visit(BinaryExpression &binary) +void UnreachableCodeRemover::visit(Conditional &cond) { - unused_nodes.erase(binary.type); - TraversingVisitor::visit(binary); + cond.body.visit(*this); + bool reachable_if_true = reachable; + reachable = true; + cond.else_body.visit(*this); + + reachable |= reachable_if_true; } -void UnusedTypeRemover::visit(TernaryExpression &ternary) +void UnreachableCodeRemover::visit(Iteration &iter) { - unused_nodes.erase(ternary.type); - TraversingVisitor::visit(ternary); + TraversingVisitor::visit(iter); + + /* Always consider code after a loop reachable, since there's no checking + for whether the loop executes. */ + reachable = true; } -void UnusedTypeRemover::visit(FunctionCall &call) + +bool UnusedTypeRemover::apply(Stage &stage) { - unused_nodes.erase(call.type); - TraversingVisitor::visit(call); + stage.content.visit(*this); + NodeRemover().apply(stage, unused_nodes); + return !unused_nodes.empty(); +} + +void UnusedTypeRemover::visit(RefPtr &expr) +{ + unused_nodes.erase(expr->type); + TraversingVisitor::visit(expr); } void UnusedTypeRemover::visit(BasicTypeDeclaration &type) @@ -899,6 +1035,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) @@ -918,7 +1055,10 @@ UnusedVariableRemover::UnusedVariableRemover(): interface_block(0), r_assignment(0), assignment_target(false), - r_side_effects(false) + r_side_effects(false), + in_struct(false), + composite_reference(false), + in_loop(0) { } bool UnusedVariableRemover::apply(Stage &s) @@ -930,10 +1070,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) @@ -966,19 +1102,90 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n var_info.referenced = true; if(!assignment_target) { + bool loop_external = false; 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); + if((*i)->in_looptoken[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) @@ -1013,6 +1238,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. */ @@ -1032,6 +1258,7 @@ void UnusedVariableRemover::record_assignment(const Assignment::Target &target, AssignmentInfo &assign_info = assignments.back(); assign_info.node = &node; assign_info.target = target; + assign_info.in_loop = in_loop; /* An assignment to the target hides any assignments to the same target or its subfields. */ @@ -1064,15 +1291,26 @@ void UnusedVariableRemover::visit(ExpressionStatement &expr) unused_nodes.insert(&expr); } +void UnusedVariableRemover::visit(StructDeclaration &strct) +{ + SetFlag set_struct(in_struct); + TraversingVisitor::visit(strct); +} + void UnusedVariableRemover::visit(VariableDeclaration &var) { + TraversingVisitor::visit(var); + + if(in_struct) + 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->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_"))); @@ -1081,21 +1319,12 @@ void UnusedVariableRemover::visit(VariableDeclaration &var) var_info.initialized = true; record_assignment(&var, *var.init_expression); } - TraversingVisitor::visit(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) @@ -1161,7 +1390,18 @@ void UnusedVariableRemover::visit(Conditional &cond) void UnusedVariableRemover::visit(Iteration &iter) { BlockVariableMap saved_vars = variables; - TraversingVisitor::visit(iter); + vector saved_refs; + swap(loop_ext_refs, saved_refs); + { + SetForScope set_loop(in_loop, in_loop+1); + TraversingVisitor::visit(iter); + } + swap(loop_ext_refs, saved_refs); + + /* Visit the external references of the loop again to record assignments + done in the loop as used. */ + for(vector::const_iterator i=saved_refs.begin(); i!=saved_refs.end(); ++i) + (*i)->visit(*this); /* Merge assignments from the iteration, without clearing previous state. Further analysis is needed to determine which parts of the iteration body