Add basic crankshaft support for slow-mode for-in to avoid disabling optimizations
authorverwaest <verwaest@chromium.org>
Fri, 10 Apr 2015 13:15:32 +0000 (06:15 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 10 Apr 2015 13:15:31 +0000 (13:15 +0000)
BUG=

Review URL: https://codereview.chromium.org/1075933003

Cr-Commit-Position: refs/heads/master@{#27748}

src/hydrogen.cc
test/mjsunit/for-in-opt.js [new file with mode: 0644]
test/mjsunit/mjsunit.status

index 04d3cc7..b9e0324 100644 (file)
@@ -5050,10 +5050,6 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
     return Bailout(kForInStatementOptimizationIsDisabled);
   }
 
-  if (stmt->for_in_type() != ForInStatement::FAST_FOR_IN) {
-    return Bailout(kForInStatementIsNotFastCase);
-  }
-
   if (!stmt->each()->IsVariableProxy() ||
       !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
     return Bailout(kForInStatementWithNonLocalEachVariable);
@@ -5064,13 +5060,42 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
   CHECK_ALIVE(VisitForValue(stmt->enumerable()));
   HValue* enumerable = Top();  // Leave enumerable at the top.
 
-  HInstruction* map = Add<HForInPrepareMap>(enumerable);
-  Add<HSimulate>(stmt->PrepareId());
-
-  HInstruction* array = Add<HForInCacheArray>(
-      enumerable, map, DescriptorArray::kEnumCacheBridgeCacheIndex);
+  HInstruction* map;
+  HInstruction* array;
+  HInstruction* enum_length;
+  bool fast = stmt->for_in_type() == ForInStatement::FAST_FOR_IN;
+  if (fast) {
+    map = Add<HForInPrepareMap>(enumerable);
+    Add<HSimulate>(stmt->PrepareId());
+
+    array = Add<HForInCacheArray>(enumerable, map,
+                                  DescriptorArray::kEnumCacheBridgeCacheIndex);
+    enum_length = Add<HMapEnumLength>(map);
+
+    HInstruction* index_cache = Add<HForInCacheArray>(
+        enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
+    HForInCacheArray::cast(array)
+        ->set_index_cache(HForInCacheArray::cast(index_cache));
+  } else {
+    Add<HSimulate>(stmt->PrepareId());
+    {
+      NoObservableSideEffectsScope no_effects(this);
+      BuildJSObjectCheck(enumerable, 0);
+    }
+    Add<HSimulate>(stmt->ToObjectId());
 
-  HInstruction* enum_length = Add<HMapEnumLength>(map);
+    map = graph()->GetConstant1();
+    Runtime::FunctionId function_id = Runtime::kGetPropertyNamesFast;
+    Add<HPushArguments>(enumerable);
+    array = Add<HCallRuntime>(isolate()->factory()->empty_string(),
+                              Runtime::FunctionForId(function_id), 1);
+    Push(array);
+    Add<HSimulate>(stmt->EnumId());
+    Drop(1);
+    Handle<Map> array_map = isolate()->factory()->fixed_array_map();
+    HValue* check = Add<HCheckMaps>(array, array_map);
+    enum_length = AddLoadFixedArrayLength(array, check);
+  }
 
   HInstruction* start_index = Add<HConstant>(0);
 
@@ -5079,13 +5104,12 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
   Push(enum_length);
   Push(start_index);
 
-  HInstruction* index_cache = Add<HForInCacheArray>(
-      enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
-  HForInCacheArray::cast(array)->set_index_cache(
-      HForInCacheArray::cast(index_cache));
-
   HBasicBlock* loop_entry = BuildLoopEntry(stmt);
 
+  // Reload the values to ensure we have up-to-date values inside of the loop.
+  // This is relevant especially for OSR where the values don't come from the
+  // computation above, but from the OSR entry block.
+  enumerable = environment()->ExpressionStackAt(4);
   HValue* index = environment()->ExpressionStackAt(0);
   HValue* limit = environment()->ExpressionStackAt(1);
 
@@ -5107,18 +5131,23 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
 
   set_current_block(loop_body);
 
-  HValue* key = Add<HLoadKeyed>(
-      environment()->ExpressionStackAt(2),  // Enum cache.
-      environment()->ExpressionStackAt(0),  // Iteration index.
-      environment()->ExpressionStackAt(0),
-      FAST_ELEMENTS);
+  HValue* key =
+      Add<HLoadKeyed>(environment()->ExpressionStackAt(2),  // Enum cache.
+                      index, index, FAST_ELEMENTS);
 
-  // Check if the expected map still matches that of the enumerable.
-  // If not just deoptimize.
-  Add<HCheckMapValue>(environment()->ExpressionStackAt(4),
-                      environment()->ExpressionStackAt(3));
-
-  Bind(each_var, key);
+  if (fast) {
+    // Check if the expected map still matches that of the enumerable.
+    // If not just deoptimize.
+    Add<HCheckMapValue>(enumerable, environment()->ExpressionStackAt(3));
+    Bind(each_var, key);
+  } else {
+    HValue* function = AddLoadJSBuiltin(Builtins::FILTER_KEY);
+    Add<HPushArguments>(enumerable, key);
+    key = Add<HInvokeFunction>(function, 2);
+    Bind(each_var, key);
+    Add<HSimulate>(stmt->AssignmentId());
+    Add<HCheckHeapObject>(key);
+  }
 
   BreakAndContinueInfo break_info(stmt, scope(), 5);
   {
diff --git a/test/mjsunit/for-in-opt.js b/test/mjsunit/for-in-opt.js
new file mode 100644 (file)
index 0000000..58344e4
--- /dev/null
@@ -0,0 +1,75 @@
+// 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: --harmony-proxies --allow-natives-syntax
+
+"use strict";
+
+// Test non-JSObject receiver.
+function f(o) {
+  var result = [];
+  for (var i in o) {
+    result.push(i);
+  }
+  return result;
+}
+
+assertEquals(["0"], f("a"));
+assertEquals(["0"], f("a"));
+%OptimizeFunctionOnNextCall(f);
+assertEquals(["0","1","2"], f("bla"));
+
+// Test the lazy deopt points.
+var keys = ["a", "b", "c", "d"];
+var has_keys = [];
+var deopt_has = false;
+var deopt_enum = false;
+
+var handler = {
+  enumerate: function(target) {
+    if (deopt_enum) {
+      %DeoptimizeFunction(f2);
+      deopt_enum = false;
+    }
+    return keys;
+  },
+
+  getPropertyDescriptor: function(k) {
+    if (deopt_has) {
+      %DeoptimizeFunction(f2);
+      deopt_has = false;
+    }
+    has_keys.push(k);
+    return {value: 10, configurable: true, writable: false, enumerable: true};
+  }
+};
+
+
+var proxy = Proxy.create(handler);
+var o = {__proto__: proxy};
+
+function f2(o) {
+  var result = [];
+  for (var i in o) {
+    result.push(i);
+  }
+  return result;
+}
+
+function check_f2() {
+  assertEquals(keys, f2(o));
+  assertEquals(keys, has_keys);
+  has_keys.length = 0;
+}
+
+check_f2();
+check_f2();
+// Test lazy deopt after GetPropertyNamesFast
+%OptimizeFunctionOnNextCall(f2);
+deopt_enum = true;
+check_f2();
+// Test lazy deopt after FILTER_KEY
+%OptimizeFunctionOnNextCall(f2);
+deopt_has = true;
+check_f2();
index d3aecb1..1e1ca7f 100644 (file)
   'regress/regress-2451': [PASS, NO_VARIANTS],  # with custom snapshot and gc-stress.
   'debug-multiple-breakpoints': [PASS, NO_VARIANTS],  # with custom snapshot and gc-stress.
 
+  # TODO(jarin): Fix for-in deopt points in turbofan.
+  'for-in-opt': [PASS, NO_VARIANTS],
+
   # TODO(mstarzinger): Optimizing top-level code flushed out some correctness
   # issues on ARM and ARM64.
   'es6/math-log2-log10': [PASS, NO_VARIANTS],  # on ARM and ARM64.