Support OSR in for-in loops.
authorvegorov@chromium.org <vegorov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 22 Feb 2012 16:45:35 +0000 (16:45 +0000)
committervegorov@chromium.org <vegorov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 22 Feb 2012 16:45:35 +0000 (16:45 +0000)
Modify PreProcessOsrEntry to work with OSR entries that have non-empty expression stack.

Modify graph builder to take for-in state from environment instead of directly referencing emitted instructions.

Extend %OptimizeFunctionOnNextCall with an argument to force OSR to make writing OSR tests easier: %OptimizeFunctionOnNextCall(f, "osr").

R=fschneider@chromium.org
TEST=test/mjsunit/compiler/optimized-for-in.js

Review URL: https://chromiumcodereview.appspot.com/9431030

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10796 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/hydrogen.cc
src/hydrogen.h
src/runtime-profiler.h
src/runtime.cc
src/runtime.h
test/mjsunit/compiler/optimized-for-in.js

index dd8b402..9918e85 100644 (file)
@@ -3058,15 +3058,24 @@ void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
 
   set_current_block(osr_entry);
   int osr_entry_id = statement->OsrEntryId();
-  // We want the correct environment at the OsrEntry instruction.  Build
-  // it explicitly.  The expression stack should be empty.
-  ASSERT(environment()->ExpressionStackIsEmpty());
-  for (int i = 0; i < environment()->length(); ++i) {
+  int first_expression_index = environment()->first_expression_index();
+  int length = environment()->length();
+  for (int i = 0; i < first_expression_index; ++i) {
     HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
     AddInstruction(osr_value);
     environment()->Bind(i, osr_value);
   }
 
+  if (first_expression_index != length) {
+    environment()->Drop(length - first_expression_index);
+    for (int i = first_expression_index; i < length; ++i) {
+      HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
+      AddInstruction(osr_value);
+      environment()->Push(osr_value);
+    }
+  }
+
+
   AddSimulate(osr_entry_id);
   AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
   HContext* context = new(zone()) HContext;
@@ -3274,15 +3283,17 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
   HForInCacheArray::cast(array)->set_index_cache(
       HForInCacheArray::cast(index_cache));
 
+  PreProcessOsrEntry(stmt);
   HBasicBlock* loop_entry = CreateLoopHeaderBlock();
   current_block()->Goto(loop_entry);
   set_current_block(loop_entry);
 
-  HValue* index = Top();
+  HValue* index = environment()->ExpressionStackAt(0);
+  HValue* limit = environment()->ExpressionStackAt(1);
 
   // Check that we still have more keys.
   HCompareIDAndBranch* compare_index =
-      new(zone()) HCompareIDAndBranch(index, array_length, Token::LT);
+      new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
   compare_index->SetInputRepresentation(Representation::Integer32());
 
   HBasicBlock* loop_body = graph()->CreateBasicBlock();
@@ -3299,11 +3310,15 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
 
   HValue* key = AddInstruction(
       new(zone()) HLoadKeyedFastElement(
-          array, index, HLoadKeyedFastElement::OMIT_HOLE_CHECK));
+          environment()->ExpressionStackAt(2),  // Enum cache.
+          environment()->ExpressionStackAt(0),  // Iteration index.
+          HLoadKeyedFastElement::OMIT_HOLE_CHECK));
 
   // Check if the expected map still matches that of the enumerable.
   // If not just deoptimize.
-  AddInstruction(new(zone()) HCheckMapValue(enumerable, map));
+  AddInstruction(new(zone()) HCheckMapValue(
+      environment()->ExpressionStackAt(4),
+      environment()->ExpressionStackAt(3)));
 
   Bind(each_var, key);
 
@@ -7440,9 +7455,8 @@ bool HEnvironment::HasExpressionAt(int index) const {
 
 
 bool HEnvironment::ExpressionStackIsEmpty() const {
-  int first_expression = parameter_count() + specials_count() + local_count();
-  ASSERT(length() >= first_expression);
-  return length() == first_expression;
+  ASSERT(length() >= first_expression_index());
+  return length() == first_expression_index();
 }
 
 
index 1226656..65aa346 100644 (file)
@@ -399,6 +399,10 @@ class HEnvironment: public ZoneObject {
     return i >= parameter_count() && i < parameter_count() + specials_count();
   }
 
+  int first_expression_index() const {
+    return parameter_count() + specials_count() + local_count();
+  }
+
   void Bind(Variable* variable, HValue* value) {
     Bind(IndexFor(variable), value);
   }
index f374566..f7ca3f0 100644 (file)
@@ -101,6 +101,8 @@ class RuntimeProfiler {
   void RemoveDeadSamples();
   void UpdateSamplesAfterCompact(ObjectVisitor* visitor);
 
+  void AttemptOnStackReplacement(JSFunction* function);
+
  private:
   static const int kSamplerWindowSize = 16;
 
@@ -108,8 +110,6 @@ class RuntimeProfiler {
 
   void Optimize(JSFunction* function, const char* reason);
 
-  void AttemptOnStackReplacement(JSFunction* function);
-
   void ClearSampleBuffer();
 
   void ClearSampleBufferNewSpaceEntries();
index 1115542..9597681 100644 (file)
@@ -8590,10 +8590,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RunningInSimulator) {
 
 RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
   HandleScope scope(isolate);
-  ASSERT(args.length() == 1);
+  RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+
   if (!function->IsOptimizable()) return isolate->heap()->undefined_value();
   function->MarkForLazyRecompilation();
+
+  Code* unoptimized = function->shared()->code();
+  if (args.length() == 2 &&
+      unoptimized->kind() == Code::FUNCTION) {
+    CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
+    CHECK(type->IsEqualTo(CStrVector("osr")));
+    isolate->runtime_profiler()->AttemptOnStackReplacement(*function);
+    unoptimized->set_allow_osr_at_loop_nesting_level(
+        Code::kMaxLoopNestingMarker);
+  }
+
   return isolate->heap()->undefined_value();
 }
 
index 73f82b2..bd6568f 100644 (file)
@@ -91,7 +91,7 @@ namespace internal {
   F(NotifyOSR, 0, 1) \
   F(DeoptimizeFunction, 1, 1) \
   F(RunningInSimulator, 0, 1) \
-  F(OptimizeFunctionOnNextCall, 1, 1) \
+  F(OptimizeFunctionOnNextCall, -1, 1) \
   F(GetOptimizationStatus, 1, 1) \
   F(GetOptimizationCount, 1, 1) \
   F(CompileForOnStackReplacement, 1, 1) \
index c8076ac..8b16101 100644 (file)
@@ -242,3 +242,57 @@ tryFunction("a1b2c3d4e5f6", function () {
   for (var i in t) r.push(i + t[i]);
   return r.join('');
 });
+
+// Test OSR inside for-in.
+function osr_inner(t, limit) {
+  var r = 1;
+  for (var x in t) {
+    for (var i = 0; i < t[x].length; i++) {
+      r += t[x][i];
+      if (i === limit) {
+        %OptimizeFunctionOnNextCall(osr_inner, "osr");
+      }
+    }
+    r += x;
+  }
+  return r;
+}
+
+function osr_outer(t, osr_after) {
+  var r = 1;
+  for (var x in t) {
+    for (var i = 0; i < t[x].length; i++) {
+      r += t[x][i];
+    }
+    if (x === osr_after) {
+      %OptimizeFunctionOnNextCall(osr_outer, "osr");
+    }
+    r += x;
+  }
+  return r;
+}
+
+function osr_outer_and_deopt(t, osr_after) {
+  var r = 1;
+  for (var x in t) {
+    r += x;
+    if (x == osr_after) {
+      %OptimizeFunctionOnNextCall(osr_outer_and_deopt, "osr");
+    }
+  }
+  return r;
+}
+
+function test_osr() {
+  with ({}) {}  // Disable optimizations of this function.
+  var arr = new Array(20);
+  for (var i = 0; i < arr.length; i++) {
+    arr[i] = i + 1;
+  }
+  arr.push(":");  // Force deopt at the end of the loop.
+  assertEquals("211:x", osr_inner({x: arr}, (arr.length / 2) | 0));
+  assertEquals("7x456y", osr_outer({x: [1,2,3], y: [4,5,6]}, "x"));
+  assertEquals("101234567", osr_outer_and_deopt([1,2,3,4,5,6,7,8], "5"));
+}
+
+test_osr();