Revision: 9833
Author:   [email protected]
Date:     Fri Oct 28 03:35:38 2011
Log:      Force transition to FAST_ELEMENTS on out-of-bounds KeyedLoads.

Proactively ensure that that objects don't get FAST_DOUBLE_ELEMENTS to reduce the number of double boxing operations when generated code calls the runtime frequently to satisfy KeyedLoad requests.

Review URL: http://codereview.chromium.org/8416014
http://code.google.com/p/v8/source/detail?r=9833

Modified:
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/test/mjsunit/unbox-double-arrays.js

=======================================
--- /branches/bleeding_edge/src/runtime.cc      Fri Oct 28 02:10:29 2011
+++ /branches/bleeding_edge/src/runtime.cc      Fri Oct 28 03:35:38 2011
@@ -4178,6 +4178,23 @@

   return Runtime::GetObjectProperty(isolate, object, key);
 }
+
+
+MaybeObject* TransitionElements(Handle<Object> object,
+                                ElementsKind to_kind,
+                                Isolate* isolate) {
+  HandleScope scope(isolate);
+  if (!object->IsJSObject()) return isolate->ThrowIllegalOperation();
+  ElementsKind from_kind =
+      Handle<JSObject>::cast(object)->map()->elements_kind();
+  if (Map::IsValidElementsTransition(from_kind, to_kind)) {
+    Handle<Object> result =
+        TransitionElementsKind(Handle<JSObject>::cast(object), to_kind);
+    if (result.is_null()) return isolate->ThrowIllegalOperation();
+    return *result;
+  }
+  return isolate->ThrowIllegalOperation();
+}


 // KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
@@ -4196,40 +4213,63 @@
   //
   // Additionally, we need to make sure that we do not cache results
   // for objects that require access checks.
-  if (args[0]->IsJSObject() &&
-      !args[0]->IsJSGlobalProxy() &&
-      !args[0]->IsAccessCheckNeeded() &&
-      args[1]->IsString()) {
-    JSObject* receiver = JSObject::cast(args[0]);
-    String* key = String::cast(args[1]);
-    if (receiver->HasFastProperties()) {
-      // Attempt to use lookup cache.
-      Map* receiver_map = receiver->map();
-      KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache();
-      int offset = keyed_lookup_cache->Lookup(receiver_map, key);
-      if (offset != -1) {
-        Object* value = receiver->FastPropertyAt(offset);
- return value->IsTheHole() ? isolate->heap()->undefined_value() : value;
-      }
- // Lookup cache miss. Perform lookup and update the cache if appropriate.
-      LookupResult result(isolate);
-      receiver->LocalLookup(key, &result);
-      if (result.IsProperty() && result.type() == FIELD) {
-        int offset = result.GetFieldIndex();
-        keyed_lookup_cache->Update(receiver_map, key, offset);
-        return receiver->FastPropertyAt(offset);
-      }
-    } else {
-      // Attempt dictionary lookup.
-      StringDictionary* dictionary = receiver->property_dictionary();
-      int entry = dictionary->FindEntry(key);
-      if ((entry != StringDictionary::kNotFound) &&
-          (dictionary->DetailsAt(entry).type() == NORMAL)) {
-        Object* value = dictionary->ValueAt(entry);
-        if (!receiver->IsGlobalObject()) return value;
-        value = JSGlobalPropertyCell::cast(value)->value();
-        if (!value->IsTheHole()) return value;
-        // If value is the hole do the general lookup.
+  if (args[0]->IsJSObject()) {
+    if (!args[0]->IsJSGlobalProxy() &&
+        !args[0]->IsAccessCheckNeeded() &&
+        args[1]->IsString()) {
+      JSObject* receiver = JSObject::cast(args[0]);
+      String* key = String::cast(args[1]);
+      if (receiver->HasFastProperties()) {
+        // Attempt to use lookup cache.
+        Map* receiver_map = receiver->map();
+ KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache();
+        int offset = keyed_lookup_cache->Lookup(receiver_map, key);
+        if (offset != -1) {
+          Object* value = receiver->FastPropertyAt(offset);
+          return value->IsTheHole()
+              ? isolate->heap()->undefined_value()
+              : value;
+        }
+        // Lookup cache miss.  Perform lookup and update the cache if
+        // appropriate.
+        LookupResult result(isolate);
+        receiver->LocalLookup(key, &result);
+        if (result.IsProperty() && result.type() == FIELD) {
+          int offset = result.GetFieldIndex();
+          keyed_lookup_cache->Update(receiver_map, key, offset);
+          return receiver->FastPropertyAt(offset);
+        }
+      } else {
+        // Attempt dictionary lookup.
+        StringDictionary* dictionary = receiver->property_dictionary();
+        int entry = dictionary->FindEntry(key);
+        if ((entry != StringDictionary::kNotFound) &&
+            (dictionary->DetailsAt(entry).type() == NORMAL)) {
+          Object* value = dictionary->ValueAt(entry);
+          if (!receiver->IsGlobalObject()) return value;
+          value = JSGlobalPropertyCell::cast(value)->value();
+          if (!value->IsTheHole()) return value;
+          // If value is the hole do the general lookup.
+        }
+      }
+    } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) {
+      // JSObject without a string key. If the key is a Smi, check for a
+ // definite out-of-bounds access to elements, which is a strong indicator
+      // that subsequent accesses will also call the runtime. Proactively
+      // transition elements to FAST_ELEMENTS to avoid excessive boxing of
+      // doubles for those future calls in the case that the elements would
+      // become FAST_DOUBLE_ELEMENTS.
+      Handle<JSObject> js_object(args.at<JSObject>(0));
+      ElementsKind elements_kind = js_object->GetElementsKind();
+      if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
+          elements_kind == FAST_DOUBLE_ELEMENTS) {
+        FixedArrayBase* elements = js_object->elements();
+        if (args.at<Smi>(1)->value() >= elements->length()) {
+          MaybeObject* maybe_object = TransitionElements(js_object,
+                                                         FAST_ELEMENTS,
+                                                         isolate);
+          if (maybe_object->IsFailure()) return maybe_object;
+        }
       }
     }
   } else if (args[0]->IsString() && args[1]->IsSmi()) {
@@ -4614,23 +4654,6 @@
                                     attributes,
                                     strict_mode);
 }
-
-
-MaybeObject* TransitionElements(Handle<Object> object,
-                                ElementsKind to_kind,
-                                Isolate* isolate) {
-  HandleScope scope(isolate);
-  if (!object->IsJSObject()) return isolate->ThrowIllegalOperation();
-  ElementsKind from_kind =
-      Handle<JSObject>::cast(object)->map()->elements_kind();
-  if (Map::IsValidElementsTransition(from_kind, to_kind)) {
-    Handle<Object> result =
-        TransitionElementsKind(Handle<JSObject>::cast(object), to_kind);
-    if (result.is_null()) return isolate->ThrowIllegalOperation();
-    return *result;
-  }
-  return isolate->ThrowIllegalOperation();
-}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/unbox-double-arrays.js Fri Jul 22 02:04:16 2011 +++ /branches/bleeding_edge/test/mjsunit/unbox-double-arrays.js Fri Oct 28 03:35:38 2011
@@ -77,8 +77,6 @@
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }
@@ -89,8 +87,6 @@
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }
@@ -101,8 +97,6 @@
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }
@@ -113,32 +107,40 @@
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }

   function test_various_loads5(a, value_5, value_6, value_7) {
     assertTrue(%HasFastDoubleElements(a));
+    if (value_5 != undefined) {
+      assertEquals(value_5, a[5]);
+    };
+    if (value_6 != undefined) {
+      assertEquals(value_6, a[6]);
+      assertEquals(value_6, a[computed_6()]); // Test non-constant key
+    }
+    assertEquals(value_7, a[7]);
+    assertEquals(large_array_size, a.length);
+    assertTrue(%HasFastDoubleElements(a));
+  }
+
+  function test_various_loads6(a, value_5, value_6, value_7) {
+    assertTrue(%HasFastDoubleElements(a));
     assertEquals(value_5, a[5]);
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }

-  function test_various_loads6(a, value_5, value_6, value_7) {
+  function test_various_loads7(a, value_5, value_6, value_7) {
     assertTrue(%HasFastDoubleElements(a));
     assertEquals(value_5, a[5]);
     assertEquals(value_6, a[6]);
     assertEquals(value_6, a[computed_6()]); // Test non-constant key
     assertEquals(value_7, a[7]);
-    assertEquals(undefined, a[large_array_size-1]);
-    assertEquals(undefined, a[-1]);
     assertEquals(large_array_size, a.length);
     assertTrue(%HasFastDoubleElements(a));
   }
@@ -248,6 +250,8 @@
                       expected_array_value(7));

   // Make sure Crankshaft code handles the hole correctly (bailout)
+  var large_array = new allocator(large_array_size);
+  force_to_fast_double_array(large_array);
   test_various_stores(large_array,
                       expected_array_value(5),
                       expected_array_value(6),
@@ -273,7 +277,12 @@
                       undefined,
                       expected_array_value(7));

+  %DeoptimizeFunction(test_various_loads6);
+  gc();
+
   // Test stores for non-NaN.
+  var large_array = new allocator(large_array_size);
+  force_to_fast_double_array(large_array);
   %OptimizeFunctionOnNextCall(test_various_stores);
   test_various_stores(large_array,
                       expected_array_value(5),
@@ -285,11 +294,23 @@
                       expected_array_value(6),
                       expected_array_value(7));

-  test_various_loads6(large_array,
+  test_various_loads7(large_array,
                       expected_array_value(5),
                       expected_array_value(6),
                       expected_array_value(7));

+  test_various_loads7(large_array,
+                      expected_array_value(5),
+                      expected_array_value(6),
+                      expected_array_value(7));
+
+  %OptimizeFunctionOnNextCall(test_various_loads7);
+
+  test_various_loads7(large_array,
+                      expected_array_value(5),
+                      expected_array_value(6),
+                      expected_array_value(7));
+
   // Test NaN behavior for stores.
   test_various_stores(large_array,
                       NaN,
@@ -301,7 +322,7 @@
                       -NaN,
                       expected_array_value(7));

-  test_various_loads6(large_array,
+  test_various_loads7(large_array,
                       NaN,
                       -NaN,
                       expected_array_value(7));
@@ -317,7 +338,7 @@
                       -Infinity,
                       expected_array_value(7));

-  test_various_loads6(large_array,
+  test_various_loads7(large_array,
                       Infinity,
                       -Infinity,
                       expected_array_value(7));
@@ -434,7 +455,6 @@
 large_array3[4] = -Infinity;

 function call_apply() {
-  assertTrue(%HasFastDoubleElements(large_array3));
   called_by_apply.apply({}, large_array3);
 }

@@ -449,7 +469,6 @@
 function test_for_in() {
   // Due to previous tests, keys 0..25 and 95 should be present.
   next_expected = 0;
-  assertTrue(%HasFastDoubleElements(large_array3));
   for (x in large_array3) {
     assertTrue(next_expected++ == x);
     if (next_expected == 25) {

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to