Revision: 11739
Author: [email protected]
Date: Fri Jun 8 06:06:24 2012
Log: Optimistically assume that elements IC only transition once.
Thanks to Zheng Liu for identifying this issue.
[email protected]
BUG=v8:2141
TEST=test/mjsunit/elements-kind.js
Review URL: https://chromiumcodereview.appspot.com/10532063
http://code.google.com/p/v8/source/detail?r=11739
Modified:
/branches/bleeding_edge/src/elements-kind.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ic.cc
/branches/bleeding_edge/src/ic.h
/branches/bleeding_edge/src/objects-printer.cc
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/stub-cache.cc
/branches/bleeding_edge/src/stub-cache.h
/branches/bleeding_edge/test/mjsunit/elements-kind.js
/branches/bleeding_edge/test/mjsunit/elements-transition-hoisting.js
=======================================
--- /branches/bleeding_edge/src/elements-kind.h Fri May 25 02:58:40 2012
+++ /branches/bleeding_edge/src/elements-kind.h Fri Jun 8 06:06:24 2012
@@ -86,6 +86,17 @@
int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind);
+inline bool IsDictionaryElementsKind(ElementsKind kind) {
+ return kind == DICTIONARY_ELEMENTS;
+}
+
+
+inline bool IsExternalArrayElementsKind(ElementsKind kind) {
+ return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
+ kind <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND;
+}
+
+
inline bool IsFastElementsKind(ElementsKind kind) {
ASSERT(FIRST_FAST_ELEMENTS_KIND == 0);
return kind <= FAST_HOLEY_DOUBLE_ELEMENTS;
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Fri Jun 8 02:21:23 2012
+++ /branches/bleeding_edge/src/hydrogen.cc Fri Jun 8 06:06:24 2012
@@ -5584,7 +5584,7 @@
AddInstruction(transition);
} else {
type_todo[map->elements_kind()] = true;
- if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
+ if (IsExternalArrayElementsKind(map->elements_kind())) {
todo_external_array = true;
}
num_untransitionable_maps++;
=======================================
--- /branches/bleeding_edge/src/ic.cc Fri Jun 8 01:48:05 2012
+++ /branches/bleeding_edge/src/ic.cc Fri Jun 8 06:06:24 2012
@@ -1581,38 +1581,43 @@
}
bool monomorphic = false;
+ bool is_transition_stub = IsTransitionStubKind(stub_kind);
+ Handle<Map> receiver_map(receiver->map());
+ Handle<Map> monomorphic_map = receiver_map;
MapHandleList target_receiver_maps;
- if (ic_state != UNINITIALIZED && ic_state != PREMONOMORPHIC) {
+ if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
+ // Optimistically assume that ICs that haven't reached the MONOMORPHIC
state
+ // yet will do so and stay there.
+ monomorphic = true;
+ } else {
GetReceiverMapsForStub(Handle<Code>(target()), &target_receiver_maps);
- }
- if (!IsTransitionStubKind(stub_kind)) {
- if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
- monomorphic = true;
- } else {
- if (ic_state == MONOMORPHIC) {
- // The first time a receiver is seen that is a transitioned
version of
- // the previous monomorphic receiver type, assume the new
ElementsKind
- // is the monomorphic type. This benefits global arrays that only
- // transition once, and all call sites accessing them are faster
if they
- // remain monomorphic. If this optimistic assumption is not true,
the IC
- // will miss again and it will become polymorphic and support both
the
- // untransitioned and transitioned maps.
- monomorphic = IsMoreGeneralElementsKindTransition(
- target_receiver_maps.at(0)->elements_kind(),
- receiver->GetElementsKind());
- }
+ if (ic_state == MONOMORPHIC && is_transition_stub) {
+ // The first time a receiver is seen that is a transitioned version
of the
+ // previous monomorphic receiver type, assume the new ElementsKind
is the
+ // monomorphic type. This benefits global arrays that only transition
+ // once, and all call sites accessing them are faster if they remain
+ // monomorphic. If this optimistic assumption is not true, the IC
will
+ // miss again and it will become polymorphic and support both the
+ // untransitioned and transitioned maps.
+ monomorphic = IsMoreGeneralElementsKindTransition(
+ target_receiver_maps.at(0)->elements_kind(),
+ receiver->GetElementsKind());
}
}
if (monomorphic) {
+ if (is_transition_stub) {
+ monomorphic_map = ComputeTransitionedMap(receiver, stub_kind);
+ ASSERT(*monomorphic_map != *receiver_map);
+ stub_kind = GetNoTransitionStubKind(stub_kind);
+ }
return ComputeMonomorphicStub(
- receiver, stub_kind, strict_mode, generic_stub);
+ monomorphic_map, stub_kind, strict_mode, generic_stub);
}
ASSERT(target() != *generic_stub);
// Determine the list of receiver maps that this call site has seen,
// adding the map that was just encountered.
- Handle<Map> receiver_map(receiver->map());
bool map_added =
AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map);
if (IsTransitionStubKind(stub_kind)) {
@@ -1673,16 +1678,16 @@
}
-Handle<Code> KeyedIC::ComputeMonomorphicStub(Handle<JSObject> receiver,
+Handle<Code> KeyedIC::ComputeMonomorphicStub(Handle<Map> receiver_map,
StubKind stub_kind,
StrictModeFlag strict_mode,
Handle<Code> generic_stub) {
- if (receiver->HasFastSmiOrObjectElements() ||
- receiver->HasExternalArrayElements() ||
- receiver->HasFastDoubleElements() ||
- receiver->HasDictionaryElements()) {
+ ElementsKind elements_kind = receiver_map->elements_kind();
+ if (IsFastElementsKind(elements_kind) ||
+ IsExternalArrayElementsKind(elements_kind) ||
+ IsDictionaryElementsKind(elements_kind)) {
return isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement(
- receiver, stub_kind, strict_mode);
+ receiver_map, stub_kind, strict_mode);
} else {
return generic_stub;
}
=======================================
--- /branches/bleeding_edge/src/ic.h Wed May 23 07:24:29 2012
+++ /branches/bleeding_edge/src/ic.h Fri Jun 8 06:06:24 2012
@@ -451,7 +451,7 @@
private:
void GetReceiverMapsForStub(Handle<Code> stub, MapHandleList* result);
- Handle<Code> ComputeMonomorphicStub(Handle<JSObject> receiver,
+ Handle<Code> ComputeMonomorphicStub(Handle<Map> receiver_map,
StubKind stub_kind,
StrictModeFlag strict_mode,
Handle<Code> default_stub);
@@ -467,6 +467,12 @@
static bool IsGrowStubKind(StubKind stub_kind) {
return stub_kind >= STORE_AND_GROW_NO_TRANSITION;
}
+
+ static StubKind GetNoTransitionStubKind(StubKind stub_kind) {
+ if (!IsTransitionStubKind(stub_kind)) return stub_kind;
+ if (IsGrowStubKind(stub_kind)) return STORE_AND_GROW_NO_TRANSITION;
+ return STORE_NO_TRANSITION;
+ }
};
=======================================
--- /branches/bleeding_edge/src/objects-printer.cc Wed May 23 07:24:29 2012
+++ /branches/bleeding_edge/src/objects-printer.cc Fri Jun 8 06:06:24 2012
@@ -559,6 +559,8 @@
prototype()->ShortPrint(out);
PrintF(out, "\n - constructor: ");
constructor()->ShortPrint(out);
+ PrintF(out, "\n - code cache: ");
+ code_cache()->ShortPrint(out);
PrintF(out, "\n");
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Fri Jun 8 00:45:11 2012
+++ /branches/bleeding_edge/src/objects.cc Fri Jun 8 06:06:24 2012
@@ -3302,14 +3302,20 @@
Map::cast(result)->SharedMapVerify();
}
if (FLAG_enable_slow_asserts) {
- // The cached map should match newly created normalized map
bit-by-bit.
+ // The cached map should match newly created normalized map
bit-by-bit,
+ // except for the code cache, which can contain some ics which can be
+ // applied to the shared map.
Object* fresh;
{ MaybeObject* maybe_fresh =
fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
if (maybe_fresh->ToObject(&fresh)) {
ASSERT(memcmp(Map::cast(fresh)->address(),
Map::cast(result)->address(),
- Map::kSize) == 0);
+ Map::kCodeCacheOffset) == 0);
+ int offset = Map::kCodeCacheOffset + kPointerSize;
+ ASSERT(memcmp(Map::cast(fresh)->address() + offset,
+ Map::cast(result)->address() + offset,
+ Map::kSize - offset) == 0);
}
}
}
@@ -5040,6 +5046,7 @@
Map::cast(result)->set_bit_field(bit_field());
Map::cast(result)->set_bit_field2(bit_field2());
Map::cast(result)->set_bit_field3(bit_field3());
+ Map::cast(result)->set_code_cache(code_cache());
Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
@@ -5077,6 +5084,8 @@
}
MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
+ ASSERT(!is_shared() || code->allowed_in_shared_map_code_cache());
+
// Allocate the code cache if not present.
if (code_cache()->IsFixedArray()) {
Object* result;
@@ -8401,6 +8410,11 @@
}
}
}
+
+
+bool Code::allowed_in_shared_map_code_cache() {
+ return is_keyed_load_stub() || is_keyed_store_stub();
+}
#ifdef ENABLE_DISASSEMBLER
=======================================
--- /branches/bleeding_edge/src/objects.h Fri Jun 8 01:48:05 2012
+++ /branches/bleeding_edge/src/objects.h Fri Jun 8 06:06:24 2012
@@ -4327,6 +4327,8 @@
inline bool has_function_cache();
inline void set_has_function_cache(bool flag);
+ bool allowed_in_shared_map_code_cache();
+
// Get the safepoint entry for the given pc.
SafepointEntry GetSafepointEntry(Address pc);
@@ -4679,13 +4681,11 @@
}
inline bool has_external_array_elements() {
- ElementsKind kind(elements_kind());
- return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
- kind <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND;
+ return IsExternalArrayElementsKind(elements_kind());
}
inline bool has_dictionary_elements() {
- return elements_kind() == DICTIONARY_ELEMENTS;
+ return IsDictionaryElementsKind(elements_kind());
}
inline bool has_slow_elements_kind() {
=======================================
--- /branches/bleeding_edge/src/stub-cache.cc Fri Jun 8 01:48:05 2012
+++ /branches/bleeding_edge/src/stub-cache.cc Fri Jun 8 06:06:24 2012
@@ -403,7 +403,7 @@
Handle<Code> StubCache::ComputeKeyedLoadOrStoreElement(
- Handle<JSObject> receiver,
+ Handle<Map> receiver_map,
KeyedIC::StubKind stub_kind,
StrictModeFlag strict_mode) {
KeyedAccessGrowMode grow_mode =
@@ -431,7 +431,6 @@
UNREACHABLE();
break;
}
- Handle<Map> receiver_map(receiver->map());
Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags));
if (probe->IsCode()) return Handle<Code>::cast(probe);
@@ -466,7 +465,7 @@
} else {
PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code,
0));
}
- JSObject::UpdateMapCodeCache(receiver, name, code);
+ Map::UpdateCodeCache(receiver_map, name, code);
return code;
}
=======================================
--- /branches/bleeding_edge/src/stub-cache.h Fri Jun 8 01:48:05 2012
+++ /branches/bleeding_edge/src/stub-cache.h Fri Jun 8 06:06:24 2012
@@ -174,7 +174,7 @@
Handle<Map> transition,
StrictModeFlag strict_mode);
- Handle<Code> ComputeKeyedLoadOrStoreElement(Handle<JSObject> receiver,
+ Handle<Code> ComputeKeyedLoadOrStoreElement(Handle<Map> receiver_map,
KeyedIC::StubKind stub_kind,
StrictModeFlag strict_mode);
=======================================
--- /branches/bleeding_edge/test/mjsunit/elements-kind.js Wed May 23
07:24:29 2012
+++ /branches/bleeding_edge/test/mjsunit/elements-kind.js Fri Jun 8
06:06:24 2012
@@ -224,9 +224,11 @@
for (var i = 0; i < 3; i++) {
convert_mixed(doubles, "three", elements_kind.fast);
}
+ convert_mixed(construct_smis(), "three", elements_kind.fast);
+ convert_mixed(construct_doubles(), "three", elements_kind.fast);
+ %OptimizeFunctionOnNextCall(convert_mixed);
smis = construct_smis();
doubles = construct_doubles();
- %OptimizeFunctionOnNextCall(convert_mixed);
convert_mixed(smis, 1, elements_kind.fast);
convert_mixed(doubles, 1, elements_kind.fast);
assertTrue(%HaveSameMap(smis, doubles));
=======================================
--- /branches/bleeding_edge/test/mjsunit/elements-transition-hoisting.js
Wed May 23 07:24:29 2012
+++ /branches/bleeding_edge/test/mjsunit/elements-transition-hoisting.js
Fri Jun 8 06:06:24 2012
@@ -58,6 +58,9 @@
}
testDoubleConversion4(new Array(5));
+ testDoubleConversion4(new Array(5)); // Call twice to make sure that
second
+ // store is a transition and not
+ // optimistically MONOMORPHIC
%OptimizeFunctionOnNextCall(testDoubleConversion4);
testDoubleConversion4(new Array(5));
testDoubleConversion4(new Array(5));
@@ -73,13 +76,16 @@
a[1] = 1;
var count = 3;
do {
- a.foo = object; // This map check should be hoistable
+ a.foo = object; // This map check should be hoistable
a[1] = object;
result = a.foo == object && a[1] == object;
} while (--count > 0);
}
testExactMapHoisting(new Array(5));
+ testExactMapHoisting(new Array(5)); // Call twice to make sure that
second
+ // store is a transition and not
+ // optimistically MONOMORPHIC
%OptimizeFunctionOnNextCall(testExactMapHoisting);
testExactMapHoisting(new Array(5));
testExactMapHoisting(new Array(5));
@@ -98,15 +104,18 @@
if (a.bar === undefined) {
a[1] = 2.5;
}
- a.foo = object; // This map check should NOT be hoistable because it
- // includes a check for the FAST_ELEMENTS map as
well as
- // the FAST_DOUBLE_ELEMENTS map, which depends on the
- // double transition above in the if, which cannot be
- // hoisted.
+ a.foo = object; // This map check should NOT be hoistable because it
+ // includes a check for the FAST_ELEMENTS map as
well as
+ // the FAST_DOUBLE_ELEMENTS map, which depends on
the
+ // double transition above in the if, which cannot
be
+ // hoisted.
} while (--count > 0);
}
testExactMapHoisting2(new Array(5));
+ testExactMapHoisting2(new Array(5)); // Call twice to make sure that
second
+ // store is a transition and not
+ // optimistically MONOMORPHIC
%OptimizeFunctionOnNextCall(testExactMapHoisting2);
testExactMapHoisting2(new Array(5));
testExactMapHoisting2(new Array(5));
@@ -123,15 +132,18 @@
var count = 3;
do {
a[1] = 2.5;
- a.foo = object; // This map check should be hoistable because all
elements
- // transitions in the loop can also be hoisted.
+ a.foo = object; // This map check should be hoistable because all
elements
+ // transitions in the loop can also be hoisted.
} while (--count > 0);
}
var add_transition = new Array(5);
add_transition.foo = 0;
- add_transition[0] = new Object(); // For FAST_ELEMENT transition to be
created
+ add_transition[0] = new Object(); // For FAST_ELEMENT transition to be
created
testExactMapHoisting3(new Array(5));
+ testExactMapHoisting3(new Array(5)); // Call twice to make sure that
second
+ // store is a transition and not
+ // optimistically MONOMORPHIC
%OptimizeFunctionOnNextCall(testExactMapHoisting3);
testExactMapHoisting3(new Array(5));
testExactMapHoisting3(new Array(5));
@@ -150,6 +162,10 @@
}
testDominatingTransitionHoisting1(new Array(5));
+ testDominatingTransitionHoisting1(new Array(5)); // Call twice to make
sure
+ // that second store
is a
+ // transition and not
+ // optimistically
MONOMORPHIC
%OptimizeFunctionOnNextCall(testDominatingTransitionHoisting1);
testDominatingTransitionHoisting1(new Array(5));
testDominatingTransitionHoisting1(new Array(5));
@@ -166,6 +182,9 @@
}
testHoistingWithSideEffect(new Array(5));
+ testHoistingWithSideEffect(new Array(5)); // Call twice to make sure
that
+ // second store is a
transition and
+ // not optimistically
MONOMORPHIC
%OptimizeFunctionOnNextCall(testHoistingWithSideEffect);
testHoistingWithSideEffect(new Array(5));
testHoistingWithSideEffect(new Array(5));
@@ -179,7 +198,7 @@
a[1] = c;
a[2] = d;
assertTrue(true);
- a[3] = e; // TransitionElementsKind should be eliminated despite
call.
+ a[3] = e; // TransitionElementsKind should be eliminated despite
call.
a[4] = f;
} while (--count > 3);
}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev