]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/optimize.cpp
Fix opcode for matrix inverse
[libs/gl.git] / source / glsl / optimize.cpp
index 927047cc7c1e387890bf4a3d1a4a784e2039c7ac..593a3222533874b6d8fde6265608efd9642e46e1 100644 (file)
@@ -1,5 +1,6 @@
 #include <msp/core/raii.h>
 #include <msp/strings/format.h>
+#include <msp/strings/utils.h>
 #include "optimize.h"
 #include "reflect.h"
 
@@ -75,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);
        }
 
@@ -89,7 +90,7 @@ void InlineableFunctionLocator::visit(FunctionDeclaration &func)
                has_out_params = ((*i)->interface=="out");
 
        unsigned &count = refcounts[func.definition];
-       if(count<=1 && !has_out_params)
+       if((count<=1 || func.source==BUILTIN_SOURCE) && !has_out_params)
                inlineable.insert(func.definition);
 
        SetForScope<FunctionDeclaration *> set(current_function, &func);
@@ -138,7 +139,7 @@ string InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_fu
        staging_block.parent = &tgt_blk;
        staging_block.variables.clear();
 
-       std::vector<RefPtr<VariableDeclaration> > params;
+       vector<RefPtr<VariableDeclaration> > params;
        params.reserve(source_func->parameters.size());
        for(NodeArray<VariableDeclaration>::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i)
        {
@@ -350,9 +351,9 @@ void FunctionInliner::visit(Iteration &iter)
 
 ExpressionInliner::ExpressionInliner():
        r_ref_info(0),
-       r_any_inlined(false),
        r_trivial(false),
-       mutating(false),
+       access_read(true),
+       access_write(false),
        iteration_init(false),
        iteration_body(0),
        r_oper(0)
@@ -361,62 +362,48 @@ ExpressionInliner::ExpressionInliner():
 bool ExpressionInliner::apply(Stage &s)
 {
        s.content.visit(*this);
-       return r_any_inlined;
-}
-
-void ExpressionInliner::inline_expression(Expression &expr, RefPtr<Expression> &ptr)
-{
-       ptr = expr.clone();
-       r_any_inlined = true;
-}
-
-void ExpressionInliner::visit(Block &block)
-{
-       TraversingVisitor::visit(block);
 
-       for(map<string, VariableDeclaration *>::iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
-       {
-               map<Assignment::Target, ExpressionInfo>::iterator j = expressions.lower_bound(i->second);
-               for(; (j!=expressions.end() && j->first.declaration==i->second); )
+       bool any_inlined = false;
+       for(list<ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
+               if(i->expression && (i->trivial || i->uses.size()==1))
                {
-                       if(j->second.expression && j->second.inline_point)
-                               inline_expression(*j->second.expression, *j->second.inline_point);
-
-                       expressions.erase(j++);
+                       for(vector<ExpressionUse>::iterator j=i->uses.begin(); j!=i->uses.end(); ++j)
+                               if(!j->blocked)
+                               {
+                                       *j->reference = i->expression->clone();
+                                       any_inlined = true;
+                               }
                }
-       }
 
-       /* Expressions assigned in this block may depend on local variables of the
-       block.  If this is a conditionally executed block, the assignments might not
-       always happen.  Mark the expressions as not available to any outer blocks. */
-       for(map<Assignment::Target, ExpressionInfo>::iterator i=expressions.begin(); i!=expressions.end(); ++i)
-               if(i->second.assign_scope==&block)
-                       i->second.available = false;
+       return any_inlined;
 }
 
 void ExpressionInliner::visit(RefPtr<Expression> &expr)
 {
        r_ref_info = 0;
        expr->visit(*this);
-       if(r_ref_info && r_ref_info->expression && r_ref_info->available)
+       if(r_ref_info && r_ref_info->expression)
        {
+               ExpressionUse use;
+               use.reference = &expr;
+               use.ref_scope = current_block;
+               use.blocked = access_write;
+
                if(iteration_body && !r_ref_info->trivial)
                {
-                       /* Don't inline non-trivial expressions which were assigned outside
-                       an iteration statement.  The iteration may run multiple times, which
+                       /* Block inlining of non-trivial expressions assigned outside an
+                       iteration statement.  The iteration may run multiple times, which
                        would cause the expression to also be evaluated multiple times. */
-                       Block *i = r_ref_info->assign_scope;
-                       for(; (i && i!=iteration_body); i=i->parent) ;
-                       if(!i)
-                               return;
+                       for(Block *i=iteration_body->parent; (!use.blocked && i); i=i->parent)
+                               use.blocked = (i==r_ref_info->assign_scope);
                }
 
-               if(r_ref_info->trivial)
-                       inline_expression(*r_ref_info->expression, expr);
-               else
-                       /* Record the inline point for a non-trivial expression but don't
-                       inline it yet.  It might turn out it shouldn't be inlined after all. */
-                       r_ref_info->inline_point = &expr;
+               /* Block inlining assignments from from inner scopes.  The assignment may
+               depend on local variables of that scope or may not always be executed. */
+               for(Block *i=r_ref_info->assign_scope->parent; (!use.blocked && i); i=i->parent)
+                       use.blocked = (i==current_block);
+
+               r_ref_info->uses.push_back(use);
        }
        r_oper = expr->oper;
        r_ref_info = 0;
@@ -424,21 +411,11 @@ void ExpressionInliner::visit(RefPtr<Expression> &expr)
 
 void ExpressionInliner::visit(VariableReference &var)
 {
-       if(var.declaration)
+       if(var.declaration && access_read)
        {
-               map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(var.declaration);
-               if(i!=expressions.end())
-               {
-                       /* If a non-trivial expression is referenced multiple times, don't
-                       inline it. */
-                       if(i->second.inline_point && !i->second.trivial)
-                               i->second.expression = 0;
-                       /* Mutating expressions are analogous to self-referencing assignments
-                       and prevent inlining. */
-                       if(mutating)
-                               i->second.expression = 0;
-                       r_ref_info = &i->second;
-               }
+               map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(var.declaration);
+               if(i!=assignments.end())
+                       r_ref_info = i->second;
        }
 }
 
@@ -456,7 +433,7 @@ void ExpressionInliner::visit(Swizzle &swizzle)
 
 void ExpressionInliner::visit(UnaryExpression &unary)
 {
-       SetFlag set_target(mutating, mutating || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
+       SetFlag set_write(access_write, access_write || unary.oper->token[1]=='+' || unary.oper->token[1]=='-');
        visit(unary.expression);
        r_trivial = false;
 }
@@ -465,7 +442,7 @@ void ExpressionInliner::visit(BinaryExpression &binary)
 {
        visit(binary.left);
        {
-               SetFlag clear_target(mutating, false);
+               SetFlag clear_write(access_write, false);
                visit(binary.right);
        }
        r_trivial = false;
@@ -474,21 +451,37 @@ void ExpressionInliner::visit(BinaryExpression &binary)
 void ExpressionInliner::visit(Assignment &assign)
 {
        {
-               SetFlag set_target(mutating);
+               SetFlag set_read(access_read, assign.oper->token[0]!='=');
+               SetFlag set_write(access_write);
                visit(assign.left);
        }
        r_oper = 0;
+       r_trivial = true;
        visit(assign.right);
 
-       map<Assignment::Target, ExpressionInfo>::iterator i = expressions.find(assign.target);
-       if(i!=expressions.end())
+       map<Assignment::Target, ExpressionInfo *>::iterator i = assignments.find(assign.target);
+       if(i!=assignments.end())
        {
-               /* Self-referencing assignments can't be inlined without additional
-               work.  Just clear any previous expression. */
-               i->second.expression = (assign.self_referencing ? 0 : assign.right.get());
-               i->second.assign_scope = current_block;
-               i->second.inline_point = 0;
-               i->second.available = true;
+               if(iteration_body && i->second->expression)
+               {
+                       /* Block inlining into previous references within the iteration
+                       statement.  On iterations after the first they would refer to the
+                       assignment within the iteration. */
+                       for(vector<ExpressionUse>::iterator j=i->second->uses.begin(); j!=i->second->uses.end(); ++j)
+                               for(Block *k=j->ref_scope; (!j->blocked && k); k=k->parent)
+                                       j->blocked = (k==iteration_body);
+               }
+
+               expressions.push_back(ExpressionInfo());
+               ExpressionInfo &info = expressions.back();
+               info.target = assign.target;
+               // Self-referencing assignments can't be inlined without additional work.
+               if(!assign.self_referencing)
+                       info.expression = assign.right;
+               info.assign_scope = current_block;
+               info.trivial = r_trivial;
+
+               i->second = &info;
        }
 
        r_trivial = false;
@@ -526,12 +519,17 @@ 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())
        {
-               ExpressionInfo &info = expressions[&var];
+               expressions.push_back(ExpressionInfo());
+               ExpressionInfo &info = expressions.back();
+               info.target = &var;
                /* Assume variables declared in an iteration initialization statement
                will have their values change throughout the iteration. */
-               info.expression = (iteration_init ? 0 : var.init_expression.get());
+               if(!iteration_init)
+                       info.expression = var.init_expression;
                info.assign_scope = current_block;
                info.trivial = r_trivial;
+
+               assignments[&var] = &info;
        }
 }
 
@@ -553,18 +551,6 @@ void ExpressionInliner::visit(Iteration &iter)
 }
 
 
-BasicTypeDeclaration::Kind ConstantFolder::get_value_kind(const Variant &value)
-{
-       if(value.check_type<bool>())
-               return BasicTypeDeclaration::BOOL;
-       else if(value.check_type<int>())
-               return BasicTypeDeclaration::INT;
-       else if(value.check_type<float>())
-               return BasicTypeDeclaration::FLOAT;
-       else
-               return BasicTypeDeclaration::VOID;
-}
-
 template<typename T>
 T ConstantFolder::evaluate_logical(char oper, T left, T right)
 {
@@ -603,6 +589,31 @@ T ConstantFolder::evaluate_arithmetic(char oper, T left, T right)
        }
 }
 
+template<typename T>
+T ConstantFolder::evaluate_int_special_op(char oper, T left, T right)
+{
+       switch(oper)
+       {
+       case '%': return left%right;
+       case '<': return left<<right;
+       case '>': return left>>right;
+       default: return T();
+       }
+}
+
+template<typename T>
+void ConstantFolder::convert_to_result(const Variant &value)
+{
+       if(value.check_type<bool>())
+               set_result(static_cast<T>(value.value<bool>()));
+       else if(value.check_type<int>())
+               set_result(static_cast<T>(value.value<int>()));
+       else if(value.check_type<unsigned>())
+               set_result(static_cast<T>(value.value<unsigned>()));
+       else if(value.check_type<float>())
+               set_result(static_cast<T>(value.value<float>()));
+}
+
 void ConstantFolder::set_result(const Variant &value, bool literal)
 {
        r_constant_value = value;
@@ -623,22 +634,27 @@ void ConstantFolder::visit(RefPtr<Expression> &expr)
        if(!r_constant || r_literal || r_uses_iter_var)
                return;
 
-       BasicTypeDeclaration::Kind kind = get_value_kind(r_constant_value);
-       if(kind==BasicTypeDeclaration::VOID)
+       RefPtr<Literal> literal = new Literal;
+       if(r_constant_value.check_type<bool>())
+               literal->token = (r_constant_value.value<bool>() ? "true" : "false");
+       else if(r_constant_value.check_type<int>())
+               literal->token = lexical_cast<string>(r_constant_value.value<int>());
+       else if(r_constant_value.check_type<unsigned>())
+               literal->token = lexical_cast<string>(r_constant_value.value<unsigned>())+"u";
+       else if(r_constant_value.check_type<float>())
+       {
+               literal->token = lexical_cast<string>(r_constant_value.value<float>(), Fmt().precision(8));
+               if(literal->token.find('.')==string::npos && literal->token.find('e')==string::npos)
+                       literal->token += ".0";
+       }
+       else
        {
                r_constant = false;
                return;
        }
-
-       RefPtr<Literal> literal = new Literal;
-       if(kind==BasicTypeDeclaration::BOOL)
-               literal->token = (r_constant_value.value<bool>() ? "true" : "false");
-       else if(kind==BasicTypeDeclaration::INT)
-               literal->token = lexical_cast<string>(r_constant_value.value<int>());
-       else if(kind==BasicTypeDeclaration::FLOAT)
-               literal->token = lexical_cast<string>(r_constant_value.value<float>());
        literal->value = r_constant_value;
        expr = literal;
+       r_any_folded = true;
 }
 
 void ConstantFolder::visit(Literal &literal)
@@ -678,25 +694,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<bool>())
                        set_result(!r_constant_value.value<bool>());
        }
        else if(oper=='~')
        {
-               if(kind==BasicTypeDeclaration::INT)
+               if(r_constant_value.check_type<int>())
                        set_result(~r_constant_value.value<int>());
+               else if(r_constant_value.check_type<unsigned>())
+                       set_result(~r_constant_value.value<unsigned>());
        }
        else if(oper=='-' && !oper2)
        {
-               if(kind==BasicTypeDeclaration::INT)
+               if(r_constant_value.check_type<int>())
                        set_result(-r_constant_value.value<int>());
-               else if(kind==BasicTypeDeclaration::FLOAT)
+               else if(r_constant_value.check_type<unsigned>())
+                       set_result(-r_constant_value.value<unsigned>());
+               else if(r_constant_value.check_type<float>())
                        set_result(-r_constant_value.value<float>());
        }
 }
@@ -716,53 +734,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<bool>())
                        set_result(evaluate_logical(oper, left_value.value<bool>(), r_constant_value.value<bool>()));
-               else if(!oper2 && left_kind==BasicTypeDeclaration::INT)
+               else if(!oper2 && left_value.check_type<int>())
                        set_result(evaluate_logical(oper, left_value.value<int>(), r_constant_value.value<int>()));
+               else if(!oper2 && left_value.check_type<unsigned>())
+                       set_result(evaluate_logical(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
        }
        else if((oper=='<' || oper=='>') && oper2!=oper)
        {
-               if(left_kind==BasicTypeDeclaration::INT)
+               if(left_value.check_type<int>())
                        set_result(evaluate_relation(binary.oper->token, left_value.value<int>(), r_constant_value.value<int>()));
-               else if(left_kind==BasicTypeDeclaration::FLOAT)
+               else if(left_value.check_type<unsigned>())
+                       set_result(evaluate_relation(binary.oper->token, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+               else if(left_value.check_type<float>())
                        set_result(evaluate_relation(binary.oper->token, left_value.value<float>(), r_constant_value.value<float>()));
        }
        else if((oper=='=' || oper=='!') && oper2=='=')
        {
-               if(left_kind==BasicTypeDeclaration::INT)
+               if(left_value.check_type<int>())
                        set_result((left_value.value<int>()==r_constant_value.value<int>()) == (oper=='='));
-               if(left_kind==BasicTypeDeclaration::FLOAT)
+               else if(left_value.check_type<unsigned>())
+                       set_result((left_value.value<unsigned>()==r_constant_value.value<unsigned>()) == (oper=='='));
+               else if(left_value.check_type<float>())
                        set_result((left_value.value<float>()==r_constant_value.value<float>()) == (oper=='='));
        }
        else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
        {
-               if(left_kind==BasicTypeDeclaration::INT)
+               if(left_value.check_type<int>())
                        set_result(evaluate_arithmetic(oper, left_value.value<int>(), r_constant_value.value<int>()));
-               else if(left_kind==BasicTypeDeclaration::FLOAT)
+               else if(left_value.check_type<unsigned>())
+                       set_result(evaluate_arithmetic(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
+               else if(left_value.check_type<float>())
                        set_result(evaluate_arithmetic(oper, left_value.value<float>(), r_constant_value.value<float>()));
        }
        else if(oper=='%' || ((oper=='<' || oper=='>') && oper2==oper))
        {
-               if(left_kind!=BasicTypeDeclaration::INT)
-                       return;
-
-               if(oper=='%')
-                       set_result(left_value.value<int>()%r_constant_value.value<int>());
-               else if(oper=='<')
-                       set_result(left_value.value<int>()<<r_constant_value.value<int>());
-               else if(oper=='>')
-                       set_result(left_value.value<int>()>>r_constant_value.value<int>());
+               if(left_value.check_type<int>())
+                       set_result(evaluate_int_special_op(oper, left_value.value<int>(), r_constant_value.value<int>()));
+               else if(left_value.check_type<unsigned>())
+                       set_result(evaluate_int_special_op(oper, left_value.value<unsigned>(), r_constant_value.value<unsigned>()));
        }
 }
 
@@ -780,6 +799,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<const BasicTypeDeclaration *>(call.type);
+               if(basic)
+               {
+                       visit(call.arguments[0]);
+                       bool can_fold = r_constant;
+                       r_constant = false;
+                       if(!can_fold)
+                               return;
+
+                       if(basic->kind==BasicTypeDeclaration::BOOL)
+                               convert_to_result<bool>(r_constant_value);
+                       else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && basic->sign)
+                               convert_to_result<int>(r_constant_value);
+                       else if(basic->kind==BasicTypeDeclaration::INT && basic->size==32 && !basic->sign)
+                               convert_to_result<unsigned>(r_constant_value);
+                       else if(basic->kind==BasicTypeDeclaration::FLOAT && basic->size==32)
+                               convert_to_result<float>(r_constant_value);
+
+                       return;
+               }
+       }
+
        TraversingVisitor::visit(call);
        r_constant = false;
 }
@@ -907,6 +950,52 @@ void ConstantConditionEliminator::visit(Iteration &iter)
 }
 
 
+UnreachableCodeRemover::UnreachableCodeRemover():
+       reachable(true)
+{ }
+
+bool UnreachableCodeRemover::apply(Stage &stage)
+{
+       stage.content.visit(*this);
+       NodeRemover().apply(stage, unreachable_nodes);
+       return !unreachable_nodes.empty();
+}
+
+void UnreachableCodeRemover::visit(Block &block)
+{
+       NodeList<Statement>::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 UnreachableCodeRemover::visit(FunctionDeclaration &func)
+{
+       TraversingVisitor::visit(func);
+       reachable = true;
+}
+
+void UnreachableCodeRemover::visit(Conditional &cond)
+{
+       cond.body.visit(*this);
+       bool reachable_if_true = reachable;
+       reachable = true;
+       cond.else_body.visit(*this);
+
+       reachable |= reachable_if_true;
+}
+
+void UnreachableCodeRemover::visit(Iteration &iter)
+{
+       TraversingVisitor::visit(iter);
+
+       /* Always consider code after a loop reachable, since there's no checking
+       for whether the loop executes. */
+       reachable = true;
+}
+
+
 bool UnusedTypeRemover::apply(Stage &stage)
 {
        stage.content.visit(*this);
@@ -964,7 +1053,9 @@ UnusedVariableRemover::UnusedVariableRemover():
        r_assignment(0),
        assignment_target(false),
        r_side_effects(false),
-       composite_reference(false)
+       in_struct(false),
+       composite_reference(false),
+       in_loop(0)
 { }
 
 bool UnusedVariableRemover::apply(Stage &s)
@@ -1008,6 +1099,7 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n
        var_info.referenced = true;
        if(!assignment_target)
        {
+               bool loop_external = false;
                for(vector<AssignmentInfo *>::const_iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
                {
                        bool covered = true;
@@ -1031,9 +1123,17 @@ void UnusedVariableRemover::referenced(const Assignment::Target &target, Node &n
                                else
                                        covered = ((*i)->target.chain[j]==target.chain[j]);
                        }
+
                        if(covered)
+                       {
                                (*i)->used_by.push_back(&node);
+                               if((*i)->in_loop<in_loop)
+                                       loop_external = true;
+                       }
                }
+
+               if(loop_external)
+                       loop_ext_refs.push_back(&node);
        }
 }
 
@@ -1155,11 +1255,12 @@ 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. */
        VariableInfo &var_info = variables[target.declaration];
-       for(unsigned i=0; i<var_info.assignments.size(); ++i)
+       for(unsigned i=0; i<var_info.assignments.size(); )
        {
                const Assignment::Target &t = var_info.assignments[i]->target;
 
@@ -1187,8 +1288,19 @@ 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;
 
@@ -1204,7 +1316,6 @@ void UnusedVariableRemover::visit(VariableDeclaration &var)
                var_info.initialized = true;
                record_assignment(&var, *var.init_expression);
        }
-       TraversingVisitor::visit(var);
 }
 
 void UnusedVariableRemover::visit(InterfaceBlock &iface)
@@ -1276,7 +1387,18 @@ void UnusedVariableRemover::visit(Conditional &cond)
 void UnusedVariableRemover::visit(Iteration &iter)
 {
        BlockVariableMap saved_vars = variables;
-       TraversingVisitor::visit(iter);
+       vector<Node *> saved_refs;
+       swap(loop_ext_refs, saved_refs);
+       {
+               SetForScope<unsigned> 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<Node *>::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