From d050c331ebd4510e7c8b871c50e33ea2564275fa Mon Sep 17 00:00:00 2001 From: titzer Date: Thu, 19 Feb 2015 03:36:38 -0800 Subject: [PATCH] [turbofan] Simplify context specialization and fix for OSR. AstGraphBuilder puts a constant context in from the beginning. Also fix bug in merging contexts in environment. R=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/934293002 Cr-Commit-Position: refs/heads/master@{#26745} --- src/compiler/ast-graph-builder.cc | 25 +++--- src/compiler/ast-graph-builder.h | 17 ++-- src/compiler/control-reducer.cc | 21 +++-- src/compiler/js-context-specialization.cc | 5 -- src/compiler/js-context-specialization.h | 5 +- src/compiler/js-inlining.cc | 2 +- src/compiler/osr.cc | 4 + src/compiler/pipeline.cc | 28 ++---- src/compiler/verifier.cc | 27 +++--- test/cctest/compiler/test-control-reducer.cc | 10 ++- .../compiler/test-js-context-specialization.cc | 6 +- test/mjsunit/compiler/osr-block-scope-func.js | 20 ----- test/mjsunit/compiler/osr-block-scope.js | 99 +++++++++++++++++++--- test/mjsunit/compiler/osr-for-let.js | 82 ++++++++++++++++++ test/mjsunit/compiler/osr-maze1.js | 1 - test/mjsunit/compiler/osr-while-let.js | 58 +++++++++++++ 16 files changed, 303 insertions(+), 107 deletions(-) create mode 100644 test/mjsunit/compiler/osr-for-let.js create mode 100644 test/mjsunit/compiler/osr-while-let.js diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index 30bdcfb..4b1fa1c 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -379,7 +379,6 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info, globals_(0, local_zone), execution_control_(nullptr), execution_context_(nullptr), - function_context_(nullptr), input_buffer_size_(0), input_buffer_(nullptr), exit_control_(nullptr), @@ -399,9 +398,10 @@ Node* AstGraphBuilder::GetFunctionClosure() { } -Node* AstGraphBuilder::GetFunctionContext() { - DCHECK(function_context_ != nullptr); - return function_context_; +void AstGraphBuilder::CreateFunctionContext(bool constant_context) { + function_context_.set(constant_context + ? jsgraph()->HeapConstant(info()->context()) + : NewOuterContextParam()); } @@ -420,7 +420,7 @@ Node* AstGraphBuilder::NewCurrentContextOsrValue() { } -bool AstGraphBuilder::CreateGraph() { +bool AstGraphBuilder::CreateGraph(bool constant_context) { Scope* scope = info()->scope(); DCHECK(graph() != NULL); @@ -442,8 +442,8 @@ bool AstGraphBuilder::CreateGraph() { } // Initialize the incoming context. - function_context_ = NewOuterContextParam(); - ContextScope incoming(this, scope, function_context_); + CreateFunctionContext(constant_context); + ContextScope incoming(this, scope, function_context_.get()); // Build receiver check for sloppy mode if necessary. // TODO(mstarzinger/verwaest): Should this be moved back into the CallIC? @@ -456,7 +456,8 @@ bool AstGraphBuilder::CreateGraph() { if (heap_slots > 0) { // Push a new inner context scope for the function. Node* closure = GetFunctionClosure(); - Node* inner_context = BuildLocalFunctionContext(function_context_, closure); + Node* inner_context = + BuildLocalFunctionContext(function_context_.get(), closure); ContextScope top_context(this, scope, inner_context); CreateGraphBody(); } else { @@ -2760,10 +2761,9 @@ Node* AstGraphBuilder::BuildLoadBuiltinsObject() { Node* AstGraphBuilder::BuildLoadGlobalObject() { - Node* context = GetFunctionContext(); const Operator* load_op = javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true); - return NewNode(load_op, context); + return NewNode(load_op, function_context_.get()); } @@ -3023,6 +3023,7 @@ void AstGraphBuilder::UpdateControlDependencyToLeaveFunction(Node* exit) { void AstGraphBuilder::Environment::Merge(Environment* other) { DCHECK(values_.size() == other->values_.size()); + // TODO(titzer): make context stack heights match. DCHECK(contexts_.size() <= other->contexts_.size()); // Nothing to do if the other environment is dead. @@ -3037,6 +3038,10 @@ void AstGraphBuilder::Environment::Merge(Environment* other) { graph()->NewNode(common()->Merge(1), arraysize(inputs), inputs, true); effect_dependency_ = other->effect_dependency_; values_ = other->values_; + // TODO(titzer): make context stack heights match. + size_t min = std::min(contexts_.size(), other->contexts_.size()); + contexts_ = other->contexts_; + contexts_.resize(min, nullptr); return; } diff --git a/src/compiler/ast-graph-builder.h b/src/compiler/ast-graph-builder.h index 515283f..ebeb6c6 100644 --- a/src/compiler/ast-graph-builder.h +++ b/src/compiler/ast-graph-builder.h @@ -31,7 +31,7 @@ class AstGraphBuilder : public AstVisitor { LoopAssignmentAnalysis* loop_assignment = NULL); // Creates a graph by visiting the entire AST. - bool CreateGraph(); + bool CreateGraph(bool constant_context); // Helpers to create new control nodes. Node* NewIfTrue() { return NewNode(common()->IfTrue()); } @@ -51,12 +51,6 @@ class AstGraphBuilder : public AstVisitor { // Visiting function for declarations list is overridden. void VisitDeclarations(ZoneList* declarations) OVERRIDE; - // Get the node that represents the outer function context. - Node* GetFunctionContext(); - - // Get the node that represents the outer function closure. - Node* GetFunctionClosure(); - private: class AstContext; class AstEffectContext; @@ -88,7 +82,7 @@ class AstGraphBuilder : public AstVisitor { // Nodes representing values in the activation record. SetOncePointer function_closure_; - Node* function_context_; + SetOncePointer function_context_; // Temporary storage for building node input lists. int input_buffer_size_; @@ -127,8 +121,15 @@ class AstGraphBuilder : public AstVisitor { void set_execution_context(ContextScope* ctx) { execution_context_ = ctx; } void set_exit_control(Node* exit) { exit_control_ = exit; } + // Create the main graph body by visiting the AST. void CreateGraphBody(); + // Create the node that represents the outer context of the function. + void CreateFunctionContext(bool constant_context); + + // Get or create the node that represents the outer function closure. + Node* GetFunctionClosure(); + // Node creation helpers. Node* NewNode(const Operator* op, bool incomplete = false) { return MakeNode(op, 0, static_cast(NULL), incomplete); diff --git a/src/compiler/control-reducer.cc b/src/compiler/control-reducer.cc index df302dd..d20c8dd 100644 --- a/src/compiler/control-reducer.cc +++ b/src/compiler/control-reducer.cc @@ -291,12 +291,23 @@ class ControlReducerImpl { } #if DEBUG // Verify that no inputs to live nodes are NULL. - for (size_t j = 0; j < nodes.size(); j++) { - Node* node = nodes[j]; - for (Node* const input : node->inputs()) { - CHECK(input); + for (Node* node : nodes) { + for (int index = 0; index < node->InputCount(); index++) { + Node* input = node->InputAt(index); + if (input == nullptr) { + std::ostringstream str; + str << "GraphError: node #" << node->id() << ":" << *node->op() + << "(input @" << index << ") == null"; + FATAL(str.str().c_str()); + } + if (input->opcode() == IrOpcode::kDead) { + std::ostringstream str; + str << "GraphError: node #" << node->id() << ":" << *node->op() + << "(input @" << index << ") == dead"; + FATAL(str.str().c_str()); + } } - for (Node* const use : node->uses()) { + for (Node* use : node->uses()) { CHECK(marked.IsReachableFromEnd(use)); } } diff --git a/src/compiler/js-context-specialization.cc b/src/compiler/js-context-specialization.cc index ffc8f1c..1bf19ac 100644 --- a/src/compiler/js-context-specialization.cc +++ b/src/compiler/js-context-specialization.cc @@ -14,11 +14,6 @@ namespace internal { namespace compiler { Reduction JSContextSpecializer::Reduce(Node* node) { - if (node == context_) { - Node* constant = jsgraph_->Constant(ctx_); - NodeProperties::ReplaceWithValue(node, constant); - return Replace(constant); - } if (node->opcode() == IrOpcode::kJSLoadContext) { return ReduceJSLoadContext(node); } diff --git a/src/compiler/js-context-specialization.h b/src/compiler/js-context-specialization.h index c524eb7..f58eaa6 100644 --- a/src/compiler/js-context-specialization.h +++ b/src/compiler/js-context-specialization.h @@ -17,8 +17,7 @@ namespace compiler { // some {LoadContext} nodes or strength reducing some {StoreContext} nodes. class JSContextSpecializer : public Reducer { public: - JSContextSpecializer(Handle ctx, JSGraph* jsgraph, Node* context) - : ctx_(ctx), jsgraph_(jsgraph), context_(context) {} + explicit JSContextSpecializer(JSGraph* jsgraph) : jsgraph_(jsgraph) {} Reduction Reduce(Node* node) OVERRIDE; @@ -27,9 +26,7 @@ class JSContextSpecializer : public Reducer { Reduction ReduceJSStoreContext(Node* node); private: - Handle ctx_; JSGraph* jsgraph_; - Node* context_; }; } } diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc index 9efa4c1..91d0823 100644 --- a/src/compiler/js-inlining.cc +++ b/src/compiler/js-inlining.cc @@ -364,7 +364,7 @@ Reduction JSInliner::TryInlineJSCall(Node* call_node, jsgraph_->javascript(), jsgraph_->machine()); AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); - graph_builder.CreateGraph(); + graph_builder.CreateGraph(false); Inlinee::UnifyReturn(&jsgraph); CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone()); diff --git a/src/compiler/osr.cc b/src/compiler/osr.cc index 69532fb..e96b047 100644 --- a/src/compiler/osr.cc +++ b/src/compiler/osr.cc @@ -103,6 +103,7 @@ static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common, if (backedges == 1) { // Simple case. Map the incoming edges to the loop to the previous copy. for (Node* node : loop_tree->HeaderNodes(loop)) { + if (!all.IsLive(node)) continue; // dead phi hanging off loop. Node* copy = mapping->at(node->id()); Node* backedge = node->InputAt(1); if (previous) backedge = previous->at(backedge->id()); @@ -119,6 +120,7 @@ static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common, Node* merge = graph->NewNode(common->Merge(backedges), backedges, &tmp_inputs[0]); for (Node* node : loop_tree->HeaderNodes(loop)) { + if (!all.IsLive(node)) continue; // dead phi hanging off loop. Node* copy = mapping->at(node->id()); if (node == loop_header) { // The entry to the loop is the merge. @@ -214,7 +216,9 @@ bool OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common, // Replace the normal entry with {Dead} and the loop entry with {Start} // and run the control reducer to clean up the graph. osr_normal_entry->ReplaceUses(dead); + osr_normal_entry->Kill(); osr_loop_entry->ReplaceUses(graph->start()); + osr_loop_entry->Kill(); // Normally the control reducer removes loops whose first input is dead, // but we need to avoid that because the osr_loop is reachable through diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 83b784f..5ec5d08 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -74,7 +74,6 @@ class PipelineData { javascript_(nullptr), jsgraph_(nullptr), typer_(nullptr), - context_node_(nullptr), schedule_(nullptr), instruction_zone_scope_(zone_pool_), instruction_zone_(instruction_zone_scope_.zone()), @@ -114,7 +113,6 @@ class PipelineData { javascript_(nullptr), jsgraph_(nullptr), typer_(nullptr), - context_node_(nullptr), schedule_(schedule), instruction_zone_scope_(zone_pool_), instruction_zone_(instruction_zone_scope_.zone()), @@ -141,7 +139,6 @@ class PipelineData { javascript_(nullptr), jsgraph_(nullptr), typer_(nullptr), - context_node_(nullptr), schedule_(nullptr), instruction_zone_scope_(zone_pool_), instruction_zone_(sequence->zone()), @@ -186,12 +183,6 @@ class PipelineData { loop_assignment_ = loop_assignment; } - Node* context_node() const { return context_node_; } - void set_context_node(Node* context_node) { - DCHECK(!context_node_); - context_node_ = context_node; - } - Schedule* schedule() const { return schedule_; } void set_schedule(Schedule* schedule) { DCHECK(!schedule_); @@ -217,7 +208,6 @@ class PipelineData { common_ = nullptr; javascript_ = nullptr; jsgraph_ = nullptr; - context_node_ = nullptr; schedule_ = nullptr; } @@ -272,7 +262,6 @@ class PipelineData { JSGraph* jsgraph_; // TODO(dcarney): make this into a ZoneObject. SmartPointer typer_; - Node* context_node_; Schedule* schedule_; // All objects in the following group of fields are allocated in @@ -330,9 +319,9 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder { source_positions_(source_positions), start_position_(info->shared_info()->start_position()) {} - bool CreateGraph() { + bool CreateGraph(bool constant_context) { SourcePositionTable::Scope pos_scope(source_positions_, start_position_); - return AstGraphBuilder::CreateGraph(); + return AstGraphBuilder::CreateGraph(constant_context); } #define DEF_VISIT(type) \ @@ -344,8 +333,6 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder { AST_NODE_LIST(DEF_VISIT) #undef DEF_VISIT - Node* GetFunctionContext() { return AstGraphBuilder::GetFunctionContext(); } - private: SourcePositionTable* source_positions_; SourcePosition start_position_; @@ -433,13 +420,11 @@ struct LoopAssignmentAnalysisPhase { struct GraphBuilderPhase { static const char* phase_name() { return "graph builder"; } - void Run(PipelineData* data, Zone* temp_zone) { + void Run(PipelineData* data, Zone* temp_zone, bool constant_context) { AstGraphBuilderWithPositions graph_builder( temp_zone, data->info(), data->jsgraph(), data->loop_assignment(), data->source_positions()); - if (graph_builder.CreateGraph()) { - data->set_context_node(graph_builder.GetFunctionContext()); - } else { + if (!graph_builder.CreateGraph(constant_context)) { data->set_compilation_failed(); } } @@ -452,8 +437,7 @@ struct ContextSpecializerPhase { void Run(PipelineData* data, Zone* temp_zone) { SourcePositionTable::Scope pos(data->source_positions(), SourcePosition::Unknown()); - JSContextSpecializer spec(data->info()->context(), data->jsgraph(), - data->context_node()); + JSContextSpecializer spec(data->jsgraph()); GraphReducer graph_reducer(data->graph(), temp_zone); AddReducer(data, &graph_reducer, &spec); graph_reducer.ReduceGraph(); @@ -918,7 +902,7 @@ Handle Pipeline::GenerateCode() { Run(); } - Run(); + Run(info()->is_context_specializing()); if (data.compilation_failed()) return Handle::null(); RunPrintAndVerify("Initial untyped", true); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 26fd693..9480afb 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -68,45 +68,44 @@ class Verifier::Visitor { void CheckNotTyped(Node* node) { if (NodeProperties::IsTyped(node)) { std::ostringstream str; - str << "TypeError: node #" << node->opcode() << ":" - << node->op()->mnemonic() << " should never have a type"; - V8_Fatal(__FILE__, __LINE__, str.str().c_str()); + str << "TypeError: node #" << node->id() << ":" << *node->op() + << " should never have a type"; + FATAL(str.str().c_str()); } } void CheckUpperIs(Node* node, Type* type) { if (typing == TYPED && !bounds(node).upper->Is(type)) { std::ostringstream str; - str << "TypeError: node #" << node->opcode() << ":" - << node->op()->mnemonic() << " upper bound "; + str << "TypeError: node #" << node->id() << ":" << *node->op() + << " upper bound "; bounds(node).upper->PrintTo(str); str << " is not "; type->PrintTo(str); - V8_Fatal(__FILE__, __LINE__, str.str().c_str()); + FATAL(str.str().c_str()); } } void CheckUpperMaybe(Node* node, Type* type) { if (typing == TYPED && !bounds(node).upper->Maybe(type)) { std::ostringstream str; - str << "TypeError: node #" << node->opcode() << ":" - << node->op()->mnemonic() << " upper bound "; + str << "TypeError: node #" << node->id() << ":" << *node->op() + << " upper bound "; bounds(node).upper->PrintTo(str); str << " must intersect "; type->PrintTo(str); - V8_Fatal(__FILE__, __LINE__, str.str().c_str()); + FATAL(str.str().c_str()); } } void CheckValueInputIs(Node* node, int i, Type* type) { Node* input = ValueInput(node, i); if (typing == TYPED && !bounds(input).upper->Is(type)) { std::ostringstream str; - str << "TypeError: node #" << node->opcode() << ":" - << node->op()->mnemonic() << "(input @" << i << " = " - << input->opcode() << ":" << input->op()->mnemonic() - << ") upper bound "; + str << "TypeError: node #" << node->id() << ":" << *node->op() + << "(input @" << i << " = " << input->opcode() << ":" + << input->op()->mnemonic() << ") upper bound "; bounds(input).upper->PrintTo(str); str << " is not "; type->PrintTo(str); - V8_Fatal(__FILE__, __LINE__, str.str().c_str()); + FATAL(str.str().c_str()); } } }; diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc index 5b71df4..827dcfd 100644 --- a/test/cctest/compiler/test-control-reducer.cc +++ b/test/cctest/compiler/test-control-reducer.cc @@ -153,6 +153,12 @@ class ControlReducerTester : HandleAndZoneScope { ReducePhiIterative(expect, phi); // iterative should give the same result. } + // Checks one-step reduction of a phi. + void ReducePhiNonIterative(Node* expect, Node* phi) { + Node* result = ControlReducer::ReducePhiForTesting(&jsgraph, &common, phi); + CHECK_EQ(expect, result); + } + void ReducePhiIterative(Node* expect, Node* phi) { p0->ReplaceInput(0, start); // hack: parameters may be trimmed. Node* ret = graph.NewNode(common.Return(), phi, start, start); @@ -484,10 +490,10 @@ TEST(CReducePhi2_dead) { for (size_t i = 1; i < kNumLeafs; i++) { Node* a = R.leaf[i], *b = R.leaf[0]; Node* phi1 = R.Phi(b, a, R.dead); - R.ReducePhi(phi1, phi1); + R.ReducePhiNonIterative(phi1, phi1); Node* phi2 = R.Phi(a, b, R.dead); - R.ReducePhi(phi2, phi2); + R.ReducePhiNonIterative(phi2, phi2); } } diff --git a/test/cctest/compiler/test-js-context-specialization.cc b/test/cctest/compiler/test-js-context-specialization.cc index f4500c7..e2cb8e6 100644 --- a/test/cctest/compiler/test-js-context-specialization.cc +++ b/test/cctest/compiler/test-js-context-specialization.cc @@ -60,7 +60,7 @@ TEST(ReduceJSLoadContext) { Node* const_context = t.jsgraph()->Constant(native); Node* deep_const_context = t.jsgraph()->Constant(subcontext2); Node* param_context = t.NewNode(t.common()->Parameter(0), start); - JSContextSpecializer spec(Handle(), t.jsgraph(), const_context); + JSContextSpecializer spec(t.jsgraph()); { // Mutable slot, constant context, depth = 0 => do nothing. @@ -132,7 +132,7 @@ TEST(ReduceJSStoreContext) { Node* const_context = t.jsgraph()->Constant(native); Node* deep_const_context = t.jsgraph()->Constant(subcontext2); Node* param_context = t.NewNode(t.common()->Parameter(0), start); - JSContextSpecializer spec(Handle(), t.jsgraph(), const_context); + JSContextSpecializer spec(t.jsgraph()); { // Mutable slot, constant context, depth = 0 => do nothing. @@ -197,7 +197,7 @@ TEST(SpecializeToContext) { Node* const_context = t.jsgraph()->Constant(native); Node* param_context = t.NewNode(t.common()->Parameter(0), start); - JSContextSpecializer spec(native, t.jsgraph(), const_context); + JSContextSpecializer spec(t.jsgraph()); { // Check that specialization replaces values and forwards effects diff --git a/test/mjsunit/compiler/osr-block-scope-func.js b/test/mjsunit/compiler/osr-block-scope-func.js index a2b88c3..df4076c 100644 --- a/test/mjsunit/compiler/osr-block-scope-func.js +++ b/test/mjsunit/compiler/osr-block-scope-func.js @@ -25,23 +25,3 @@ function foo() { assertEquals(4950, foo()()); assertEquals(4950, foo()()); assertEquals(4950, foo()()); - -function bar() { - var result; - { - let sum = 0; - for (let i = 0; i < 90; i++) { - sum += i; - if (i == 45) %OptimizeOsr(); - } - result = ret; - function ret() { - return sum; - } - } - return result; -} - -assertEquals(4005, bar()()); -assertEquals(4005, bar()()); -assertEquals(4005, bar()()); diff --git a/test/mjsunit/compiler/osr-block-scope.js b/test/mjsunit/compiler/osr-block-scope.js index f67eb9d..0d78cdc 100644 --- a/test/mjsunit/compiler/osr-block-scope.js +++ b/test/mjsunit/compiler/osr-block-scope.js @@ -6,12 +6,41 @@ "use strict"; +function nest(body, name, depth) { + var header = ""; + for (var i = 0; i < depth; i++) { + var x = "x" + (i + 1); + header += " for(var " + x + " = 0; " + x + " < 2; " + x + " = " + x + " + 1 | 0) {\n"; + body = body + "}" + } + + return body.replace(new RegExp("function " + name + "\\(\\) {"), + "function " + name + "_" + x + "() {\n" + header); +} + +function test(expected, func, depth) { + assertEquals(expected, func()); + assertEquals(expected, func()); + assertEquals(expected, func()); + + var orig = func.toString(); + var name = func.name; + for (var depth = 1; depth < 4; depth++) { + var body = nest(orig, name, depth); + func = eval("(" + body + ")"); + + assertEquals(expected, func()); + assertEquals(expected, func()); + assertEquals(expected, func()); + } +} + function foo() { var result; { let sum = 0; - for (var i = 0; i < 100; i++) { - if (i == 50) %OptimizeOsr(); + for (var i = 0; i < 10; i++) { + %OptimizeOsr(); sum += i; } result = sum; @@ -19,23 +48,69 @@ function foo() { return result; } -assertEquals(4950, foo()); -assertEquals(4950, foo()); -assertEquals(4950, foo()); +test(45, foo); function bar() { - var result; + let sum = 0; + for (var i = 0; i < 10; i++) { + %OptimizeOsr(); + sum += i; + } + return sum; +} + +test(45, bar); + +function bon() { { let sum = 0; - for (let i = 0; i < 90; i++) { + for (var i = 0; i < 10; i++) { + if (i == 5) %OptimizeOsr(); sum += i; - if (i == 45) %OptimizeOsr(); } - result = sum; + return sum; + } +} + +test(45, bon); + +function row() { + var i = 0; + { + let sum = 0; + while (true) { + if (i == 8) return sum; + %OptimizeOsr(); + sum = i; + i = i + 1 | 0; + } + } + return 11; +} + +test(7, row); + +function nub() { + let i = 0; + while (i < 2) { + %OptimizeOsr(); + i++; + } + return i; +} + +test(2, nub); + +function kub() { + var result = 0; + let i = 0; + while (i < 2) { + let x = i; + %OptimizeOsr(); + i++; + result = x; } return result; } -assertEquals(4005, bar()); -assertEquals(4005, bar()); -assertEquals(4005, bar()); +test(1, kub); diff --git a/test/mjsunit/compiler/osr-for-let.js b/test/mjsunit/compiler/osr-for-let.js new file mode 100644 index 0000000..4b2fa3e --- /dev/null +++ b/test/mjsunit/compiler/osr-for-let.js @@ -0,0 +1,82 @@ +// Copyright 2015 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. + +// Flags: --allow-natives-syntax --use-osr --turbo-osr + +"use strict"; + +function test(expected, func) { + assertEquals(expected, func()); + assertEquals(expected, func()); + assertEquals(expected, func()); +} + +function bar() { + var result; + { + let sum = 0; + for (let i = 0; i < 90; i++) { + sum += i; + if (i == 45) %OptimizeOsr(); + } + result = sum; + } + return result; +} + +test(4005, bar); + +function baz() { + let sum = 0; + for (let i = 0; i < 2; i++) { + sum = 2; + %OptimizeOsr(); + } + return sum; +} + +test(2, baz); + +function qux() { + var result = 0; + for (let i = 0; i < 2; i++) { + result = i; + %OptimizeOsr(); + } + return result; +} + +test(1, qux); + +function nux() { + var result = 0; + for (let i = 0; i < 2; i++) { + { + let sum = i; + %OptimizeOsr(); + result = sum; + } + } + return result; +} + +test(1, nux); + +function blo() { + var result; + { + let sum = 0; + for (let i = 0; i < 90; i++) { + sum += i; + if (i == 45) %OptimizeOsr(); + } + result = ret; + function ret() { + return sum; + } + } + return result; +} + +test(4005, blo()); diff --git a/test/mjsunit/compiler/osr-maze1.js b/test/mjsunit/compiler/osr-maze1.js index 87d85c8..6e192c1 100644 --- a/test/mjsunit/compiler/osr-maze1.js +++ b/test/mjsunit/compiler/osr-maze1.js @@ -42,7 +42,6 @@ function bar(goal) { } } } - print(count); return sum; } diff --git a/test/mjsunit/compiler/osr-while-let.js b/test/mjsunit/compiler/osr-while-let.js new file mode 100644 index 0000000..c19cf6c --- /dev/null +++ b/test/mjsunit/compiler/osr-while-let.js @@ -0,0 +1,58 @@ +// Copyright 2015 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. + +// Flags: --allow-natives-syntax --use-osr --turbo-osr + +"use strict"; + +function test(expected, func) { + assertEquals(expected, func()); + assertEquals(expected, func()); + assertEquals(expected, func()); +} + +function foo() { + var result = 0; + { + let x = 0; + var temp_x = x; + var first = 1; + outer: while (true) { + let x = temp_x; + if (first == 1) first = 0; + else x = x + 1 | 0; + var flag = 1; + for (; flag == 1; (flag = 0, temp_x = x)) { + if (x < 2) { + result = x; %OptimizeOsr(); + } else { + break outer; + } + } + if (flag == 1) break; + } + } + return result; +} + +test(1, foo); + + +function smo() { + var result = 0; + { + let x = 11; + outer: while (true) { + let y = x; + for (var i = 0; i < 5; i++) { + %OptimizeOsr(); + if (i) break outer; + else result = y; + } + } + } + return result; +} + +test(11, smo); -- 2.7.4