#include "src/compiler/ast-graph-builder.h"
#include "src/compiler.h"
+#include "src/compiler/ast-loop-assignment-analyzer.h"
#include "src/compiler/control-builders.h"
#include "src/compiler/machine-operator.h"
-#include "src/compiler/node-properties.h"
#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/node-properties.h"
#include "src/full-codegen.h"
#include "src/parser.h"
#include "src/scopes.h"
jsgraph_(jsgraph),
globals_(0, local_zone),
breakable_(NULL),
- execution_context_(NULL) {
+ execution_context_(NULL),
+ loop_assignment_analysis_(NULL) {
InitializeAstVisitor(local_zone);
}
int parameter_count = info()->num_parameters();
graph()->SetStart(graph()->NewNode(common()->Start(parameter_count)));
+ if (FLAG_loop_assignment_analysis) {
+ // TODO(turbofan): use a temporary zone for the loop assignment analysis.
+ AstLoopAssignmentAnalyzer analyzer(zone(), info());
+ loop_assignment_analysis_ = analyzer.Analyze();
+ }
+
// Initialize the top-level environment.
Environment env(this, scope, graph()->start());
set_environment(&env);
}
+BitVector* AstGraphBuilder::GetVariablesAssignedInLoop(
+ IterationStatement* stmt) {
+ if (loop_assignment_analysis_ == NULL) return NULL;
+ return loop_assignment_analysis_->GetVariablesAssignedInLoop(stmt);
+}
+
+
void AstGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder while_loop(this);
- while_loop.BeginLoop();
+ while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
VisitIterationBody(stmt, &while_loop, 0);
while_loop.EndBody();
VisitForTest(stmt->cond());
void AstGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
LoopBuilder while_loop(this);
- while_loop.BeginLoop();
+ while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
VisitForTest(stmt->cond());
Node* condition = environment()->Pop();
while_loop.BreakUnless(condition);
void AstGraphBuilder::VisitForStatement(ForStatement* stmt) {
LoopBuilder for_loop(this);
VisitIfNotNull(stmt->init());
- for_loop.BeginLoop();
+ for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
if (stmt->cond() != NULL) {
VisitForTest(stmt->cond());
Node* condition = environment()->Pop();
environment()->Push(jsgraph()->ZeroConstant());
// PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
LoopBuilder for_loop(this);
- for_loop.BeginLoop();
+ for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
// Check loop termination condition.
Node* index = environment()->Peek(0);
Node* exit_cond =
namespace compiler {
class ControlBuilder;
-class LoopBuilder;
class Graph;
+class LoopAssignmentAnalysis;
+class LoopBuilder;
// The AstGraphBuilder produces a high-level IR graph, based on an
// underlying AST. The produced graph can either be compiled into a
SetOncePointer<Node> function_closure_;
SetOncePointer<Node> function_context_;
+ LoopAssignmentAnalysis* loop_assignment_analysis_;
+
CompilationInfo* info() const { return info_; }
inline StrictMode strict_mode() const;
JSGraph* jsgraph() { return jsgraph_; }
OutputFrameStateCombine StateCombineFromAstContext();
+ BitVector* GetVariablesAssignedInLoop(IterationStatement* stmt);
+
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
DISALLOW_COPY_AND_ASSIGN(AstGraphBuilder);
};
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/ast-loop-assignment-analyzer.h"
+#include "src/parser.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+typedef class AstLoopAssignmentAnalyzer ALAA; // for code shortitude.
+
+ALAA::AstLoopAssignmentAnalyzer(Zone* zone, CompilationInfo* info)
+ : info_(info), loop_stack_(zone) {
+ InitializeAstVisitor(zone);
+}
+
+
+LoopAssignmentAnalysis* ALAA::Analyze() {
+ LoopAssignmentAnalysis* a = new (zone()) LoopAssignmentAnalysis(zone());
+ result_ = a;
+ VisitStatements(info()->function()->body());
+ result_ = NULL;
+ return a;
+}
+
+
+void ALAA::Enter(IterationStatement* loop) {
+ int num_variables = 1 + info()->scope()->num_parameters() +
+ info()->scope()->num_stack_slots();
+ BitVector* bits = new (zone()) BitVector(num_variables, zone());
+ loop_stack_.push_back(bits);
+}
+
+
+void ALAA::Exit(IterationStatement* loop) {
+ DCHECK(loop_stack_.size() > 0);
+ BitVector* bits = loop_stack_.back();
+ loop_stack_.pop_back();
+ if (!loop_stack_.empty()) {
+ loop_stack_.back()->Union(*bits);
+ }
+ result_->list_.push_back(
+ std::pair<IterationStatement*, BitVector*>(loop, bits));
+}
+
+
+// ---------------------------------------------------------------------------
+// -- Leaf nodes -------------------------------------------------------------
+// ---------------------------------------------------------------------------
+
+void ALAA::VisitVariableDeclaration(VariableDeclaration* leaf) {}
+void ALAA::VisitFunctionDeclaration(FunctionDeclaration* leaf) {}
+void ALAA::VisitModuleDeclaration(ModuleDeclaration* leaf) {}
+void ALAA::VisitImportDeclaration(ImportDeclaration* leaf) {}
+void ALAA::VisitExportDeclaration(ExportDeclaration* leaf) {}
+void ALAA::VisitModuleVariable(ModuleVariable* leaf) {}
+void ALAA::VisitModulePath(ModulePath* leaf) {}
+void ALAA::VisitModuleUrl(ModuleUrl* leaf) {}
+void ALAA::VisitEmptyStatement(EmptyStatement* leaf) {}
+void ALAA::VisitContinueStatement(ContinueStatement* leaf) {}
+void ALAA::VisitBreakStatement(BreakStatement* leaf) {}
+void ALAA::VisitDebuggerStatement(DebuggerStatement* leaf) {}
+void ALAA::VisitFunctionLiteral(FunctionLiteral* leaf) {}
+void ALAA::VisitNativeFunctionLiteral(NativeFunctionLiteral* leaf) {}
+void ALAA::VisitVariableProxy(VariableProxy* leaf) {}
+void ALAA::VisitLiteral(Literal* leaf) {}
+void ALAA::VisitRegExpLiteral(RegExpLiteral* leaf) {}
+void ALAA::VisitThisFunction(ThisFunction* leaf) {}
+void ALAA::VisitSuperReference(SuperReference* leaf) {}
+
+
+// ---------------------------------------------------------------------------
+// -- Pass-through nodes------------------------------------------------------
+// ---------------------------------------------------------------------------
+void ALAA::VisitModuleLiteral(ModuleLiteral* e) { Visit(e->body()); }
+
+
+void ALAA::VisitBlock(Block* stmt) { VisitStatements(stmt->statements()); }
+
+
+void ALAA::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void ALAA::VisitIfStatement(IfStatement* stmt) {
+ Visit(stmt->condition());
+ Visit(stmt->then_statement());
+ Visit(stmt->else_statement());
+}
+
+
+void ALAA::VisitReturnStatement(ReturnStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void ALAA::VisitWithStatement(WithStatement* stmt) {
+ Visit(stmt->expression());
+ Visit(stmt->statement());
+}
+
+
+void ALAA::VisitSwitchStatement(SwitchStatement* stmt) {
+ Visit(stmt->tag());
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ for (int i = 0; i < clauses->length(); i++) {
+ Visit(clauses->at(i));
+ }
+}
+
+
+void ALAA::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ Visit(stmt->try_block());
+ Visit(stmt->finally_block());
+}
+
+
+void ALAA::VisitClassLiteral(ClassLiteral* e) {
+ VisitIfNotNull(e->extends());
+ VisitIfNotNull(e->constructor());
+ ZoneList<ObjectLiteralProperty*>* properties = e->properties();
+ for (int i = 0; i < properties->length(); i++) {
+ Visit(properties->at(i)->value());
+ }
+}
+
+
+void ALAA::VisitConditional(Conditional* e) {
+ Visit(e->condition());
+ Visit(e->then_expression());
+ Visit(e->else_expression());
+}
+
+
+void ALAA::VisitObjectLiteral(ObjectLiteral* e) {
+ ZoneList<ObjectLiteralProperty*>* properties = e->properties();
+ for (int i = 0; i < properties->length(); i++) {
+ Visit(properties->at(i)->value());
+ }
+}
+
+
+void ALAA::VisitArrayLiteral(ArrayLiteral* e) { VisitExpressions(e->values()); }
+
+
+void ALAA::VisitYield(Yield* stmt) {
+ Visit(stmt->generator_object());
+ Visit(stmt->expression());
+}
+
+
+void ALAA::VisitThrow(Throw* stmt) { Visit(stmt->exception()); }
+
+
+void ALAA::VisitProperty(Property* e) {
+ Visit(e->obj());
+ Visit(e->key());
+}
+
+
+void ALAA::VisitCall(Call* e) {
+ Visit(e->expression());
+ VisitExpressions(e->arguments());
+}
+
+
+void ALAA::VisitCallNew(CallNew* e) {
+ Visit(e->expression());
+ VisitExpressions(e->arguments());
+}
+
+
+void ALAA::VisitCallRuntime(CallRuntime* e) {
+ VisitExpressions(e->arguments());
+}
+
+
+void ALAA::VisitUnaryOperation(UnaryOperation* e) { Visit(e->expression()); }
+
+
+void ALAA::VisitBinaryOperation(BinaryOperation* e) {
+ Visit(e->left());
+ Visit(e->right());
+}
+
+
+void ALAA::VisitCompareOperation(CompareOperation* e) {
+ Visit(e->left());
+ Visit(e->right());
+}
+
+
+void ALAA::VisitCaseClause(CaseClause* cc) {
+ if (!cc->is_default()) Visit(cc->label());
+ VisitStatements(cc->statements());
+}
+
+
+// ---------------------------------------------------------------------------
+// -- Interesting nodes-------------------------------------------------------
+// ---------------------------------------------------------------------------
+void ALAA::VisitModuleStatement(ModuleStatement* stmt) {
+ Visit(stmt->body());
+ // TODO(turbofan): can a module appear in a loop?
+ AnalyzeAssignment(stmt->proxy()->var());
+}
+
+
+void ALAA::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ Visit(stmt->try_block());
+ Visit(stmt->catch_block());
+ // TODO(turbofan): are catch variables well-scoped?
+ AnalyzeAssignment(stmt->variable());
+}
+
+
+void ALAA::VisitDoWhileStatement(DoWhileStatement* loop) {
+ Enter(loop);
+ Visit(loop->body());
+ Visit(loop->cond());
+ Exit(loop);
+}
+
+
+void ALAA::VisitWhileStatement(WhileStatement* loop) {
+ Enter(loop);
+ Visit(loop->cond());
+ Visit(loop->body());
+ Exit(loop);
+}
+
+
+void ALAA::VisitForStatement(ForStatement* loop) {
+ VisitIfNotNull(loop->init());
+ Enter(loop);
+ VisitIfNotNull(loop->cond());
+ Visit(loop->body());
+ VisitIfNotNull(loop->next());
+ Exit(loop);
+}
+
+
+void ALAA::VisitForInStatement(ForInStatement* loop) {
+ Enter(loop);
+ Visit(loop->each());
+ Visit(loop->subject());
+ Visit(loop->body());
+ Exit(loop);
+}
+
+
+void ALAA::VisitForOfStatement(ForOfStatement* loop) {
+ Enter(loop);
+ Visit(loop->each());
+ Visit(loop->subject());
+ Visit(loop->body());
+ Exit(loop);
+}
+
+
+void ALAA::VisitAssignment(Assignment* stmt) {
+ Expression* l = stmt->target();
+ Visit(l);
+ Visit(stmt->value());
+ if (l->IsVariableProxy()) AnalyzeAssignment(l->AsVariableProxy()->var());
+}
+
+
+void ALAA::VisitCountOperation(CountOperation* e) {
+ Expression* l = e->expression();
+ Visit(l);
+ if (l->IsVariableProxy()) AnalyzeAssignment(l->AsVariableProxy()->var());
+}
+
+
+void ALAA::AnalyzeAssignment(Variable* var) {
+ if (!loop_stack_.empty() && var->IsStackAllocated()) {
+ loop_stack_.back()->Add(GetVariableIndex(info()->scope(), var));
+ }
+}
+
+
+int ALAA::GetVariableIndex(Scope* scope, Variable* var) {
+ CHECK(var->IsStackAllocated());
+ if (var->is_this()) return 0;
+ if (var->IsParameter()) return 1 + var->index();
+ return 1 + scope->num_parameters() + var->index();
+}
+
+
+int LoopAssignmentAnalysis::GetAssignmentCountForTesting(Scope* scope,
+ Variable* var) {
+ int count = 0;
+ int var_index = AstLoopAssignmentAnalyzer::GetVariableIndex(scope, var);
+ for (size_t i = 0; i < list_.size(); i++) {
+ if (list_[i].second->Contains(var_index)) count++;
+ }
+ return count;
+}
+}
+}
+} // namespace v8::internal::compiler
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_AST_LOOP_ASSIGNMENT_ANALYZER_H_
+#define V8_COMPILER_AST_LOOP_ASSIGNMENT_ANALYZER_H_
+
+#include "src/ast.h"
+#include "src/data-flow.h"
+#include "src/v8.h"
+#include "src/zone-containers.h"
+
+namespace v8 {
+namespace internal {
+
+class Variable;
+class Scope;
+
+namespace compiler {
+
+// The result of analyzing loop assignments.
+class LoopAssignmentAnalysis : public ZoneObject {
+ public:
+ BitVector* GetVariablesAssignedInLoop(IterationStatement* loop) {
+ for (size_t i = 0; i < list_.size(); i++) {
+ // TODO(turbofan): hashmap or binary search for loop assignments.
+ if (list_[i].first == loop) return list_[i].second;
+ }
+ UNREACHABLE(); // should never ask for loops that aren't here!
+ return NULL;
+ }
+
+ int GetAssignmentCountForTesting(Scope* scope, Variable* var);
+
+ private:
+ friend class AstLoopAssignmentAnalyzer;
+ explicit LoopAssignmentAnalysis(Zone* zone) : list_(zone) {}
+ ZoneVector<std::pair<IterationStatement*, BitVector*>> list_;
+};
+
+
+// The class that performs loop assignment analysis by walking the AST.
+class AstLoopAssignmentAnalyzer : public AstVisitor {
+ public:
+ AstLoopAssignmentAnalyzer(Zone* zone, CompilationInfo* info);
+
+ LoopAssignmentAnalysis* Analyze();
+
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ static int GetVariableIndex(Scope* scope, Variable* var);
+
+ private:
+ CompilationInfo* info_;
+ ZoneDeque<BitVector*> loop_stack_;
+ LoopAssignmentAnalysis* result_;
+
+ CompilationInfo* info() { return info_; }
+
+ void Enter(IterationStatement* loop);
+ void Exit(IterationStatement* loop);
+
+ void VisitIfNotNull(AstNode* node) {
+ if (node != NULL) Visit(node);
+ }
+
+ void AnalyzeAssignment(Variable* var);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+ DISALLOW_COPY_AND_ASSIGN(AstLoopAssignmentAnalyzer);
+};
+}
+}
+} // namespace v8::internal::compiler
+
+#endif // V8_COMPILER_AST_LOOP_ASSIGNMENT_ANALYZER_H_
}
-void LoopBuilder::BeginLoop() {
+void LoopBuilder::BeginLoop(BitVector* assigned) {
builder_->NewLoop();
- loop_environment_ = environment()->CopyForLoop();
+ loop_environment_ = environment()->CopyForLoop(assigned);
continue_environment_ = environment()->CopyAsUnreachable();
break_environment_ = environment()->CopyAsUnreachable();
}
break_environment_(NULL) {}
// Primitive control commands.
- void BeginLoop();
+ void BeginLoop(BitVector* assigned);
void EndBody();
void EndLoop();
}
-void StructuredGraphBuilder::Environment::PrepareForLoop() {
+void StructuredGraphBuilder::Environment::PrepareForLoop(BitVector* assigned) {
Node* control = GetControlDependency();
- for (int i = 0; i < static_cast<int>(values()->size()); ++i) {
- Node* phi = builder_->NewPhi(1, values()->at(i), control);
- values()->at(i) = phi;
+ int size = static_cast<int>(values()->size());
+ if (assigned == NULL) {
+ // Assume that everything is updated in the loop.
+ for (int i = 0; i < size; ++i) {
+ Node* phi = builder_->NewPhi(1, values()->at(i), control);
+ values()->at(i) = phi;
+ }
+ } else {
+ // Only build phis for those locals assigned in this loop.
+ for (int i = 0; i < size; ++i) {
+ if (i < assigned->length() && !assigned->Contains(i)) continue;
+ Node* phi = builder_->NewPhi(1, values()->at(i), control);
+ values()->at(i) = phi;
+ }
}
Node* effect = builder_->NewEffectPhi(1, GetEffectDependency(), control);
UpdateEffectDependency(effect);
namespace v8 {
namespace internal {
+
+class BitVector;
+
namespace compiler {
class Node;
}
// Copies this environment at a loop header control-flow point.
- Environment* CopyForLoop() {
- PrepareForLoop();
+ Environment* CopyForLoop(BitVector* assigned) {
+ PrepareForLoop(assigned);
return builder()->CopyEnvironment(this);
}
NodeVector* values() { return &values_; }
// Prepare environment to be used as loop header.
- void PrepareForLoop();
+ void PrepareForLoop(BitVector* assigned);
private:
StructuredGraphBuilder* builder_;
DEFINE_BOOL(turbo_inlining_intrinsics, false,
"enable inlining of intrinsics in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
+DEFINE_BOOL(loop_assignment_analysis, true, "perform loop assignment analysis")
DEFINE_IMPLICATION(turbo_inlining_intrinsics, turbo_inlining)
DEFINE_IMPLICATION(turbo_inlining, turbo_types)
DEFINE_BOOL(turbo_profiling, false, "enable profiling in TurboFan")
'compiler/test-js-constant-cache.cc',
'compiler/test-js-typed-lowering.cc',
'compiler/test-linkage.cc',
+ 'compiler/test-loop-assignment-analysis.cc',
'compiler/test-machine-operator-reducer.cc',
'compiler/test-node-algorithm.cc',
'compiler/test-node-cache.cc',
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/ast-loop-assignment-analyzer.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+namespace {
+const int kBufferSize = 1024;
+
+struct TestHelper : public HandleAndZoneScope {
+ char buffer[kBufferSize];
+ Handle<JSFunction> function;
+ LoopAssignmentAnalysis* result;
+
+ explicit TestHelper(const char* body)
+ : function(Handle<JSFunction>::null()), result(NULL) {
+ snprintf(buffer, kBufferSize, "function f(a,b,c) { %s; } f;", body);
+ v8::Local<v8::Value> v = CompileRun(buffer);
+ Handle<Object> obj = v8::Utils::OpenHandle(*v);
+ function = Handle<JSFunction>::cast(obj);
+ }
+
+ void CheckLoopAssignedCount(int expected, const char* var_name) {
+ // TODO(titzer): don't scope analyze every single time.
+ CompilationInfo info(function, main_zone());
+
+ CHECK(Parser::Parse(&info));
+ CHECK(Rewriter::Rewrite(&info));
+ CHECK(Scope::Analyze(&info));
+
+ Scope* scope = info.function()->scope();
+ AstValueFactory* factory = info.ast_value_factory();
+ CHECK_NE(NULL, scope);
+
+ if (result == NULL) {
+ AstLoopAssignmentAnalyzer analyzer(main_zone(), &info);
+ result = analyzer.Analyze();
+ CHECK_NE(NULL, result);
+ }
+
+ const i::AstRawString* name = factory->GetOneByteString(var_name);
+
+ i::Variable* var = scope->Lookup(name);
+ CHECK_NE(NULL, var);
+
+ if (var->location() == Variable::UNALLOCATED) {
+ CHECK_EQ(0, expected);
+ } else {
+ CHECK(var->IsStackAllocated());
+ CHECK_EQ(expected, result->GetAssignmentCountForTesting(scope, var));
+ }
+ }
+};
+}
+
+
+TEST(SimpleLoop1) {
+ TestHelper f("var x = 0; while (x) ;");
+
+ f.CheckLoopAssignedCount(0, "x");
+}
+
+
+TEST(SimpleLoop2) {
+ const char* loops[] = {
+ "while (x) { var x = 0; }", "for(;;) { var x = 0; }",
+ "for(;x;) { var x = 0; }", "for(;x;x) { var x = 0; }",
+ "for(var i = x; x; x) { var x = 0; }", "for(y in 0) { var x = 0; }",
+ "for(y of 0) { var x = 0; }", "for(var x = 0; x; x++) { }",
+ "for(var x = 0; x++;) { }", "var x; for(;x;x++) { }",
+ "var x; do { x = 1; } while (0);", "do { var x = 1; } while (0);"};
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "x");
+ }
+}
+
+
+TEST(ForInOf1) {
+ const char* loops[] = {
+ "for(x in 0) { }", "for(x of 0) { }",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(0, "x");
+ }
+}
+
+
+TEST(Param1) {
+ TestHelper f("while (1) a = 0;");
+
+ f.CheckLoopAssignedCount(1, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2) {
+ TestHelper f("for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2b) {
+ TestHelper f("a; b; c; for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param3) {
+ TestHelper f("for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(Param3b) {
+ TestHelper f("a; b; c; for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(NestedLoop1) {
+ TestHelper f("while (x) { while (x) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop2) {
+ TestHelper f("while (0) { while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop3) {
+ TestHelper f("while (0) { var y = 1; while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+ f.CheckLoopAssignedCount(1, "y");
+}
+
+
+TEST(NestedInc1) {
+ const char* loops[] = {
+ "while (1) a(b++);",
+ "while (1) a(0, b++);",
+ "while (1) a(0, 0, b++);",
+ "while (1) a(b++, 1, 1);",
+ "while (1) a(++b);",
+ "while (1) a + (b++);",
+ "while (1) (b++) + a;",
+ "while (1) a + c(b++);",
+ "while (1) throw b++;",
+ "while (1) switch (b++) {} ;",
+ "while (1) switch (a) {case (b++): 0; } ;",
+ "while (1) switch (a) {case b: b++; } ;",
+ "while (1) a == (b++);",
+ "while (1) a === (b++);",
+ "while (1) +(b++);",
+ "while (1) ~(b++);",
+ "while (1) new a(b++);",
+ "while (1) (b++).f;",
+ "while (1) a[b++];",
+ "while (1) (b++)();",
+ "while (1) [b++];",
+ "while (1) [0,b++];",
+ "while (1) var y = [11,b++,12];",
+ "while (1) var y = {f:11,g:(b++),h:12};",
+ "while (1) try {b++;} finally {};",
+ "while (1) try {} finally {b++};",
+ "while (1) try {b++;} catch (e) {};",
+ "while (1) try {} catch (e) {b++};",
+ "while (1) return b++;",
+ "while (1) (b++) ? b : b;",
+ "while (1) b ? (b++) : b;",
+ "while (1) b ? b : (b++);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedAssign1) {
+ const char* loops[] = {
+ "while (1) a(b=1);",
+ "while (1) a(0, b=1);",
+ "while (1) a(0, 0, b=1);",
+ "while (1) a(b=1, 1, 1);",
+ "while (1) a + (b=1);",
+ "while (1) (b=1) + a;",
+ "while (1) a + c(b=1);",
+ "while (1) throw b=1;",
+ "while (1) switch (b=1) {} ;",
+ "while (1) switch (a) {case b=1: 0; } ;",
+ "while (1) switch (a) {case b: b=1; } ;",
+ "while (1) a == (b=1);",
+ "while (1) a === (b=1);",
+ "while (1) +(b=1);",
+ "while (1) ~(b=1);",
+ "while (1) new a(b=1);",
+ "while (1) (b=1).f;",
+ "while (1) a[b=1];",
+ "while (1) (b=1)();",
+ "while (1) [b=1];",
+ "while (1) [0,b=1];",
+ "while (1) var z = [11,b=1,12];",
+ "while (1) var y = {f:11,g:(b=1),h:12};",
+ "while (1) try {b=1;} finally {};",
+ "while (1) try {} finally {b=1};",
+ "while (1) try {b=1;} catch (e) {};",
+ "while (1) try {} catch (e) {b=1};",
+ "while (1) return b=1;",
+ "while (1) (b=1) ? b : b;",
+ "while (1) b ? (b=1) : b;",
+ "while (1) b ? b : (b=1);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedLoops3) {
+ TestHelper f("var x, y, z, w; while (x++) while (y++) while (z++) ; w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3b) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) { x=1; while (1) { y=1; while (1) z=1; } }"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3c) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) {"
+ " x++;"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ "}"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(3, "y");
+ f.CheckLoopAssignedCount(5, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
'../../src/compiler/access-builder.h',
'../../src/compiler/ast-graph-builder.cc',
'../../src/compiler/ast-graph-builder.h',
+ '../../src/compiler/ast-loop-assignment-analyzer.cc',
+ '../../src/compiler/ast-loop-assignment-analyzer.h',
'../../src/compiler/basic-block-instrumentor.cc',
'../../src/compiler/basic-block-instrumentor.h',
'../../src/compiler/change-lowering.cc',