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