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) { ...@@ -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. // KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) { RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
NoHandleAllocation ha; NoHandleAllocation ha;
...@@ -4196,40 +4213,63 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) { ...@@ -4196,40 +4213,63 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
// //
// Additionally, we need to make sure that we do not cache results // Additionally, we need to make sure that we do not cache results
// for objects that require access checks. // for objects that require access checks.
if (args[0]->IsJSObject() && if (args[0]->IsJSObject()) {
!args[0]->IsJSGlobalProxy() && if (!args[0]->IsJSGlobalProxy() &&
!args[0]->IsAccessCheckNeeded() && !args[0]->IsAccessCheckNeeded() &&
args[1]->IsString()) { args[1]->IsString()) {
JSObject* receiver = JSObject::cast(args[0]); JSObject* receiver = JSObject::cast(args[0]);
String* key = String::cast(args[1]); String* key = String::cast(args[1]);
if (receiver->HasFastProperties()) { if (receiver->HasFastProperties()) {
// Attempt to use lookup cache. // Attempt to use lookup cache.
Map* receiver_map = receiver->map(); Map* receiver_map = receiver->map();
KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache(); KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache();
int offset = keyed_lookup_cache->Lookup(receiver_map, key); int offset = keyed_lookup_cache->Lookup(receiver_map, key);
if (offset != -1) { if (offset != -1) {
Object* value = receiver->FastPropertyAt(offset); Object* value = receiver->FastPropertyAt(offset);
return value->IsTheHole() ? isolate->heap()->undefined_value() : value; return value->IsTheHole()
} ? isolate->heap()->undefined_value()
// Lookup cache miss. Perform lookup and update the cache if appropriate. : value;
LookupResult result(isolate); }
receiver->LocalLookup(key, &result); // Lookup cache miss. Perform lookup and update the cache if
if (result.IsProperty() && result.type() == FIELD) { // appropriate.
int offset = result.GetFieldIndex(); LookupResult result(isolate);
keyed_lookup_cache->Update(receiver_map, key, offset); receiver->LocalLookup(key, &result);
return receiver->FastPropertyAt(offset); 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 { } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) {
// Attempt dictionary lookup. // JSObject without a string key. If the key is a Smi, check for a
StringDictionary* dictionary = receiver->property_dictionary(); // definite out-of-bounds access to elements, which is a strong indicator
int entry = dictionary->FindEntry(key); // that subsequent accesses will also call the runtime. Proactively
if ((entry != StringDictionary::kNotFound) && // transition elements to FAST_ELEMENTS to avoid excessive boxing of
(dictionary->DetailsAt(entry).type() == NORMAL)) { // doubles for those future calls in the case that the elements would
Object* value = dictionary->ValueAt(entry); // become FAST_DOUBLE_ELEMENTS.
if (!receiver->IsGlobalObject()) return value; Handle<JSObject> js_object(args.at<JSObject>(0));
value = JSGlobalPropertyCell::cast(value)->value(); ElementsKind elements_kind = js_object->GetElementsKind();
if (!value->IsTheHole()) return value; if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
// If value is the hole do the general lookup. 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()) { } else if (args[0]->IsString() && args[1]->IsSmi()) {
...@@ -4616,23 +4656,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) { ...@@ -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) { RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) {
NoHandleAllocation ha; NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 1); RUNTIME_ASSERT(args.length() == 1);
......
...@@ -77,8 +77,6 @@ function testOneArrayType(allocator) { ...@@ -77,8 +77,6 @@ function testOneArrayType(allocator) {
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); assertTrue(%HasFastDoubleElements(a));
} }
...@@ -89,8 +87,6 @@ function testOneArrayType(allocator) { ...@@ -89,8 +87,6 @@ function testOneArrayType(allocator) {
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); assertTrue(%HasFastDoubleElements(a));
} }
...@@ -101,8 +97,6 @@ function testOneArrayType(allocator) { ...@@ -101,8 +97,6 @@ function testOneArrayType(allocator) {
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); assertTrue(%HasFastDoubleElements(a));
} }
...@@ -113,32 +107,40 @@ function testOneArrayType(allocator) { ...@@ -113,32 +107,40 @@ function testOneArrayType(allocator) {
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); assertTrue(%HasFastDoubleElements(a));
} }
function test_various_loads5(a, value_5, value_6, value_7) { 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)); assertTrue(%HasFastDoubleElements(a));
assertEquals(value_5, a[5]); assertEquals(value_5, a[5]);
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); 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)); assertTrue(%HasFastDoubleElements(a));
assertEquals(value_5, a[5]); assertEquals(value_5, a[5]);
assertEquals(value_6, a[6]); assertEquals(value_6, a[6]);
assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_6, a[computed_6()]); // Test non-constant key
assertEquals(value_7, a[7]); assertEquals(value_7, a[7]);
assertEquals(undefined, a[large_array_size-1]);
assertEquals(undefined, a[-1]);
assertEquals(large_array_size, a.length); assertEquals(large_array_size, a.length);
assertTrue(%HasFastDoubleElements(a)); assertTrue(%HasFastDoubleElements(a));
} }
...@@ -248,6 +250,8 @@ function testOneArrayType(allocator) { ...@@ -248,6 +250,8 @@ function testOneArrayType(allocator) {
expected_array_value(7)); expected_array_value(7));
// Make sure Crankshaft code handles the hole correctly (bailout) // 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, test_various_stores(large_array,
expected_array_value(5), expected_array_value(5),
expected_array_value(6), expected_array_value(6),
...@@ -273,7 +277,12 @@ function testOneArrayType(allocator) { ...@@ -273,7 +277,12 @@ function testOneArrayType(allocator) {
undefined, undefined,
expected_array_value(7)); expected_array_value(7));
%DeoptimizeFunction(test_various_loads6);
gc();
// Test stores for non-NaN. // Test stores for non-NaN.
var large_array = new allocator(large_array_size);
force_to_fast_double_array(large_array);
%OptimizeFunctionOnNextCall(test_various_stores); %OptimizeFunctionOnNextCall(test_various_stores);
test_various_stores(large_array, test_various_stores(large_array,
expected_array_value(5), expected_array_value(5),
...@@ -285,7 +294,19 @@ function testOneArrayType(allocator) { ...@@ -285,7 +294,19 @@ function testOneArrayType(allocator) {
expected_array_value(6), expected_array_value(6),
expected_array_value(7)); 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(5),
expected_array_value(6), expected_array_value(6),
expected_array_value(7)); expected_array_value(7));
...@@ -301,7 +322,7 @@ function testOneArrayType(allocator) { ...@@ -301,7 +322,7 @@ function testOneArrayType(allocator) {
-NaN, -NaN,
expected_array_value(7)); expected_array_value(7));
test_various_loads6(large_array, test_various_loads7(large_array,
NaN, NaN,
-NaN, -NaN,
expected_array_value(7)); expected_array_value(7));
...@@ -317,7 +338,7 @@ function testOneArrayType(allocator) { ...@@ -317,7 +338,7 @@ function testOneArrayType(allocator) {
-Infinity, -Infinity,
expected_array_value(7)); expected_array_value(7));
test_various_loads6(large_array, test_various_loads7(large_array,
Infinity, Infinity,
-Infinity, -Infinity,
expected_array_value(7)); expected_array_value(7));
...@@ -434,7 +455,6 @@ large_array3[3] = Infinity; ...@@ -434,7 +455,6 @@ large_array3[3] = Infinity;
large_array3[4] = -Infinity; large_array3[4] = -Infinity;
function call_apply() { function call_apply() {
assertTrue(%HasFastDoubleElements(large_array3));
called_by_apply.apply({}, large_array3); called_by_apply.apply({}, large_array3);
} }
...@@ -449,7 +469,6 @@ call_apply(); ...@@ -449,7 +469,6 @@ call_apply();
function test_for_in() { function test_for_in() {
// Due to previous tests, keys 0..25 and 95 should be present. // Due to previous tests, keys 0..25 and 95 should be present.
next_expected = 0; next_expected = 0;
assertTrue(%HasFastDoubleElements(large_array3));
for (x in large_array3) { for (x in large_array3) {
assertTrue(next_expected++ == x); assertTrue(next_expected++ == x);
if (next_expected == 25) { 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