Commit 6d7d6d4e authored by danno@chromium.org's avatar danno@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9833 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a9a97d01
......@@ -4180,6 +4180,23 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetProperty) {
}
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.
RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
NoHandleAllocation ha;
......@@ -4196,40 +4213,63 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
//
// 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);
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 {
// 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()) {
......@@ -4616,23 +4656,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) {
}
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) {
NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 1);
......
......@@ -77,8 +77,6 @@ function testOneArrayType(allocator) {
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 @@ function testOneArrayType(allocator) {
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 @@ function testOneArrayType(allocator) {
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 @@ function testOneArrayType(allocator) {
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 @@ function testOneArrayType(allocator) {
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 @@ function testOneArrayType(allocator) {
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,7 +294,19 @@ function testOneArrayType(allocator) {
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));
......@@ -301,7 +322,7 @@ function testOneArrayType(allocator) {
-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 @@ function testOneArrayType(allocator) {
-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[3] = Infinity;
large_array3[4] = -Infinity;
function call_apply() {
assertTrue(%HasFastDoubleElements(large_array3));
called_by_apply.apply({}, large_array3);
}
......@@ -449,7 +469,6 @@ call_apply();
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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment