[turbofan] Simplify context specialization and fix for OSR.
authortitzer <titzer@chromium.org>
Thu, 19 Feb 2015 11:36:38 +0000 (03:36 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 19 Feb 2015 11:36:50 +0000 (11:36 +0000)
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}

16 files changed:
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/control-reducer.cc
src/compiler/js-context-specialization.cc
src/compiler/js-context-specialization.h
src/compiler/js-inlining.cc
src/compiler/osr.cc
src/compiler/pipeline.cc
src/compiler/verifier.cc
test/cctest/compiler/test-control-reducer.cc
test/cctest/compiler/test-js-context-specialization.cc
test/mjsunit/compiler/osr-block-scope-func.js
test/mjsunit/compiler/osr-block-scope.js
test/mjsunit/compiler/osr-for-let.js [new file with mode: 0644]
test/mjsunit/compiler/osr-maze1.js
test/mjsunit/compiler/osr-while-let.js [new file with mode: 0644]

index 30bdcfb..4b1fa1c 100644 (file)
@@ -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;
   }
 
index 515283f..ebeb6c6 100644 (file)
@@ -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<Declaration*>* 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<Node> function_closure_;
-  Node* function_context_;
+  SetOncePointer<Node> 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<Node**>(NULL), incomplete);
index df302dd..d20c8dd 100644 (file)
@@ -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));
       }
     }
index ffc8f1c..1bf19ac 100644 (file)
@@ -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);
   }
index c524eb7..f58eaa6 100644 (file)
@@ -17,8 +17,7 @@ namespace compiler {
 // some {LoadContext} nodes or strength reducing some {StoreContext} nodes.
 class JSContextSpecializer : public Reducer {
  public:
-  JSContextSpecializer(Handle<Context> 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<Context> ctx_;
   JSGraph* jsgraph_;
-  Node* context_;
 };
 }
 }
index 9efa4c1..91d0823 100644 (file)
@@ -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());
index 69532fb..e96b047 100644 (file)
@@ -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
index 83b784f..5ec5d08 100644 (file)
@@ -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> 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<Code> Pipeline::GenerateCode() {
     Run<LoopAssignmentAnalysisPhase>();
   }
 
-  Run<GraphBuilderPhase>();
+  Run<GraphBuilderPhase>(info()->is_context_specializing());
   if (data.compilation_failed()) return Handle<Code>::null();
   RunPrintAndVerify("Initial untyped", true);
 
index 26fd693..9480afb 100644 (file)
@@ -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());
     }
   }
 };
index 5b71df4..827dcfd 100644 (file)
@@ -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);
   }
 }
 
index f4500c7..e2cb8e6 100644 (file)
@@ -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<Context>(), 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<Context>(), 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
index a2b88c3..df4076c 100644 (file)
@@ -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()());
index f67eb9d..0d78cdc 100644 (file)
@@ -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 (file)
index 0000000..4b2fa3e
--- /dev/null
@@ -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());
index 87d85c8..6e192c1 100644 (file)
@@ -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 (file)
index 0000000..c19cf6c
--- /dev/null
@@ -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);