[runtime] Ensure that all elements kind transitions are chained to the root map.
This CL also updates the elements kind transition lookup logic: 1) First we go back to the root map, 2) Follow elements kind transitions, 3) Replay the property transitions. BUG=v8:5009 LOG=Y TBR=bmeurer@chromium.org Review-Url: https://codereview.chromium.org/2015513002 Cr-Commit-Position: refs/heads/master@{#36652}
This commit is contained in:
parent
fa9756d08b
commit
9fa206e1f4
@ -192,12 +192,12 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
|
|||||||
MapTransitionList transitions(maps.length());
|
MapTransitionList transitions(maps.length());
|
||||||
for (Handle<Map> map : maps) {
|
for (Handle<Map> map : maps) {
|
||||||
if (Map::TryUpdate(map).ToHandle(&map)) {
|
if (Map::TryUpdate(map).ToHandle(&map)) {
|
||||||
Handle<Map> transition_target =
|
Map* transition_target =
|
||||||
Map::FindTransitionedMap(map, &possible_transition_targets);
|
map->FindElementsKindTransitionedMap(&possible_transition_targets);
|
||||||
if (transition_target.is_null()) {
|
if (transition_target == nullptr) {
|
||||||
receiver_maps.Add(map);
|
receiver_maps.Add(map);
|
||||||
} else {
|
} else {
|
||||||
transitions.push_back(std::make_pair(map, transition_target));
|
transitions.push_back(std::make_pair(map, handle(transition_target)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7670,9 +7670,13 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
|||||||
// Get transition target for each map (NULL == no transition).
|
// Get transition target for each map (NULL == no transition).
|
||||||
for (int i = 0; i < maps->length(); ++i) {
|
for (int i = 0; i < maps->length(); ++i) {
|
||||||
Handle<Map> map = maps->at(i);
|
Handle<Map> map = maps->at(i);
|
||||||
Handle<Map> transitioned_map =
|
Map* transitioned_map =
|
||||||
Map::FindTransitionedMap(map, &possible_transitioned_maps);
|
map->FindElementsKindTransitionedMap(&possible_transitioned_maps);
|
||||||
transition_target.Add(transitioned_map);
|
if (transitioned_map != nullptr) {
|
||||||
|
transition_target.Add(handle(transitioned_map));
|
||||||
|
} else {
|
||||||
|
transition_target.Add(Handle<Map>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MapHandleList untransitionable_maps(maps->length());
|
MapHandleList untransitionable_maps(maps->length());
|
||||||
|
@ -83,8 +83,11 @@ void PropertyICCompiler::CompileKeyedStorePolymorphicHandlers(
|
|||||||
for (int i = 0; i < receiver_maps->length(); ++i) {
|
for (int i = 0; i < receiver_maps->length(); ++i) {
|
||||||
Handle<Map> receiver_map(receiver_maps->at(i));
|
Handle<Map> receiver_map(receiver_maps->at(i));
|
||||||
Handle<Code> cached_stub;
|
Handle<Code> cached_stub;
|
||||||
Handle<Map> transitioned_map =
|
Handle<Map> transitioned_map;
|
||||||
Map::FindTransitionedMap(receiver_map, receiver_maps);
|
{
|
||||||
|
Map* tmap = receiver_map->FindElementsKindTransitionedMap(receiver_maps);
|
||||||
|
if (tmap != nullptr) transitioned_map = handle(tmap);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(mvstanton): The code below is doing pessimistic elements
|
// TODO(mvstanton): The code below is doing pessimistic elements
|
||||||
// transitions. I would like to stop doing that and rely on Allocation Site
|
// transitions. I would like to stop doing that and rely on Allocation Site
|
||||||
|
11
src/ic/ic.cc
11
src/ic/ic.cc
@ -762,11 +762,12 @@ bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) {
|
|||||||
ElementsKind target_elements_kind = target_map->elements_kind();
|
ElementsKind target_elements_kind = target_map->elements_kind();
|
||||||
bool more_general_transition = IsMoreGeneralElementsKindTransition(
|
bool more_general_transition = IsMoreGeneralElementsKindTransition(
|
||||||
source_map->elements_kind(), target_elements_kind);
|
source_map->elements_kind(), target_elements_kind);
|
||||||
Map* transitioned_map =
|
Map* transitioned_map = nullptr;
|
||||||
more_general_transition
|
if (more_general_transition) {
|
||||||
? source_map->LookupElementsTransitionMap(target_elements_kind)
|
MapHandleList map_list;
|
||||||
: NULL;
|
map_list.Add(handle(target_map));
|
||||||
|
transitioned_map = source_map->FindElementsKindTransitionedMap(&map_list);
|
||||||
|
}
|
||||||
return transitioned_map == target_map;
|
return transitioned_map == target_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "src/conversions-inl.h"
|
#include "src/conversions-inl.h"
|
||||||
#include "src/factory.h"
|
#include "src/factory.h"
|
||||||
#include "src/field-index-inl.h"
|
#include "src/field-index-inl.h"
|
||||||
|
#include "src/field-type.h"
|
||||||
#include "src/handles-inl.h"
|
#include "src/handles-inl.h"
|
||||||
#include "src/heap/heap-inl.h"
|
#include "src/heap/heap-inl.h"
|
||||||
#include "src/heap/heap.h"
|
#include "src/heap/heap.h"
|
||||||
@ -2811,6 +2812,25 @@ FixedArrayBase* Map::GetInitialElements() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
Handle<Map> Map::ReconfigureProperty(Handle<Map> map, int modify_index,
|
||||||
|
PropertyKind new_kind,
|
||||||
|
PropertyAttributes new_attributes,
|
||||||
|
Representation new_representation,
|
||||||
|
Handle<FieldType> new_field_type,
|
||||||
|
StoreMode store_mode) {
|
||||||
|
return Reconfigure(map, map->elements_kind(), modify_index, new_kind,
|
||||||
|
new_attributes, new_representation, new_field_type,
|
||||||
|
store_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
Handle<Map> Map::ReconfigureElementsKind(Handle<Map> map,
|
||||||
|
ElementsKind new_elements_kind) {
|
||||||
|
return Reconfigure(map, new_elements_kind, -1, kData, NONE,
|
||||||
|
Representation::None(), FieldType::None(map->GetIsolate()),
|
||||||
|
ALLOW_IN_DESCRIPTOR);
|
||||||
|
}
|
||||||
|
|
||||||
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
|
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
|
||||||
DCHECK(descriptor_number < number_of_descriptors());
|
DCHECK(descriptor_number < number_of_descriptors());
|
||||||
|
142
src/objects.cc
142
src/objects.cc
@ -3215,10 +3215,10 @@ int Map::NumberOfFields() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Handle<Map> Map::CopyGeneralizeAllRepresentations(
|
Handle<Map> Map::CopyGeneralizeAllRepresentations(
|
||||||
Handle<Map> map, int modify_index, StoreMode store_mode, PropertyKind kind,
|
Handle<Map> map, ElementsKind elements_kind, int modify_index,
|
||||||
PropertyAttributes attributes, const char* reason) {
|
StoreMode store_mode, PropertyKind kind, PropertyAttributes attributes,
|
||||||
|
const char* reason) {
|
||||||
Isolate* isolate = map->GetIsolate();
|
Isolate* isolate = map->GetIsolate();
|
||||||
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate);
|
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate);
|
||||||
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
|
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
|
||||||
@ -3274,6 +3274,7 @@ Handle<Map> Map::CopyGeneralizeAllRepresentations(
|
|||||||
MaybeHandle<Object>());
|
MaybeHandle<Object>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new_map->set_elements_kind(elements_kind);
|
||||||
return new_map;
|
return new_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3526,9 +3527,9 @@ static inline Handle<FieldType> GetFieldType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reconfigures elements kind to |new_elements_kind| and/or property at
|
||||||
// Reconfigures property at |modify_index| with |new_kind|, |new_attributes|,
|
// |modify_index| with |new_kind|, |new_attributes|, |store_mode| and/or
|
||||||
// |store_mode| and/or |new_representation|/|new_field_type|.
|
// |new_representation|/|new_field_type|.
|
||||||
// If |modify_index| is negative then no properties are reconfigured but the
|
// If |modify_index| is negative then no properties are reconfigured but the
|
||||||
// map is migrated to the up-to-date non-deprecated state.
|
// map is migrated to the up-to-date non-deprecated state.
|
||||||
//
|
//
|
||||||
@ -3538,6 +3539,7 @@ static inline Handle<FieldType> GetFieldType(
|
|||||||
// any potential new (partial) version of the type in the transition tree.
|
// any potential new (partial) version of the type in the transition tree.
|
||||||
// To do this, on each rewrite:
|
// To do this, on each rewrite:
|
||||||
// - Search the root of the transition tree using FindRootMap.
|
// - Search the root of the transition tree using FindRootMap.
|
||||||
|
// - Find/create a |root_map| with requested |new_elements_kind|.
|
||||||
// - Find |target_map|, the newest matching version of this map using the
|
// - Find |target_map|, the newest matching version of this map using the
|
||||||
// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at
|
// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at
|
||||||
// |modify_index| is considered to be of |new_kind| and having
|
// |modify_index| is considered to be of |new_kind| and having
|
||||||
@ -3553,12 +3555,13 @@ static inline Handle<FieldType> GetFieldType(
|
|||||||
// Return it.
|
// Return it.
|
||||||
// - Otherwise, invalidate the outdated transition target from |target_map|, and
|
// - Otherwise, invalidate the outdated transition target from |target_map|, and
|
||||||
// replace its transition tree with a new branch for the updated descriptors.
|
// replace its transition tree with a new branch for the updated descriptors.
|
||||||
Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
Handle<Map> Map::Reconfigure(Handle<Map> old_map,
|
||||||
PropertyKind new_kind,
|
ElementsKind new_elements_kind, int modify_index,
|
||||||
PropertyAttributes new_attributes,
|
PropertyKind new_kind,
|
||||||
Representation new_representation,
|
PropertyAttributes new_attributes,
|
||||||
Handle<FieldType> new_field_type,
|
Representation new_representation,
|
||||||
StoreMode store_mode) {
|
Handle<FieldType> new_field_type,
|
||||||
|
StoreMode store_mode) {
|
||||||
DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet.
|
DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet.
|
||||||
DCHECK(store_mode != FORCE_FIELD || modify_index >= 0);
|
DCHECK(store_mode != FORCE_FIELD || modify_index >= 0);
|
||||||
Isolate* isolate = old_map->GetIsolate();
|
Isolate* isolate = old_map->GetIsolate();
|
||||||
@ -3573,7 +3576,8 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
|||||||
// uninitialized value for representation None can be overwritten by both
|
// uninitialized value for representation None can be overwritten by both
|
||||||
// smi and tagged values. Doubles, however, would require a box allocation.
|
// smi and tagged values. Doubles, however, would require a box allocation.
|
||||||
if (modify_index >= 0 && !new_representation.IsNone() &&
|
if (modify_index >= 0 && !new_representation.IsNone() &&
|
||||||
!new_representation.IsDouble()) {
|
!new_representation.IsDouble() &&
|
||||||
|
old_map->elements_kind() == new_elements_kind) {
|
||||||
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
||||||
Representation old_representation = old_details.representation();
|
Representation old_representation = old_details.representation();
|
||||||
|
|
||||||
@ -3606,38 +3610,39 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
|||||||
// Check the state of the root map.
|
// Check the state of the root map.
|
||||||
Handle<Map> root_map(old_map->FindRootMap(), isolate);
|
Handle<Map> root_map(old_map->FindRootMap(), isolate);
|
||||||
if (!old_map->EquivalentToForTransition(*root_map)) {
|
if (!old_map->EquivalentToForTransition(*root_map)) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, new_elements_kind, modify_index, store_mode, new_kind,
|
||||||
"GenAll_NotEquivalent");
|
new_attributes, "GenAll_NotEquivalent");
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementsKind from_kind = root_map->elements_kind();
|
ElementsKind from_kind = root_map->elements_kind();
|
||||||
ElementsKind to_kind = old_map->elements_kind();
|
ElementsKind to_kind = new_elements_kind;
|
||||||
// TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
|
// TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
|
||||||
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
|
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
|
||||||
|
to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
|
||||||
to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
|
to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
|
||||||
!(IsTransitionableFastElementsKind(from_kind) &&
|
!(IsTransitionableFastElementsKind(from_kind) &&
|
||||||
IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
|
IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_InvalidElementsTransition");
|
"GenAll_InvalidElementsTransition");
|
||||||
}
|
}
|
||||||
int root_nof = root_map->NumberOfOwnDescriptors();
|
int root_nof = root_map->NumberOfOwnDescriptors();
|
||||||
if (modify_index >= 0 && modify_index < root_nof) {
|
if (modify_index >= 0 && modify_index < root_nof) {
|
||||||
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
||||||
if (old_details.kind() != new_kind ||
|
if (old_details.kind() != new_kind ||
|
||||||
old_details.attributes() != new_attributes) {
|
old_details.attributes() != new_attributes) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_RootModification1");
|
"GenAll_RootModification1");
|
||||||
}
|
}
|
||||||
if ((old_details.type() != DATA && store_mode == FORCE_FIELD) ||
|
if ((old_details.type() != DATA && store_mode == FORCE_FIELD) ||
|
||||||
(old_details.type() == DATA &&
|
(old_details.type() == DATA &&
|
||||||
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) ||
|
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) ||
|
||||||
!new_representation.fits_into(old_details.representation())))) {
|
!new_representation.fits_into(old_details.representation())))) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_RootModification2");
|
"GenAll_RootModification2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3691,9 +3696,9 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
|||||||
if (next_kind == kAccessor &&
|
if (next_kind == kAccessor &&
|
||||||
!EqualImmutableValues(old_descriptors->GetValue(i),
|
!EqualImmutableValues(old_descriptors->GetValue(i),
|
||||||
tmp_descriptors->GetValue(i))) {
|
tmp_descriptors->GetValue(i))) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_Incompatible");
|
"GenAll_Incompatible");
|
||||||
}
|
}
|
||||||
if (next_location == kField && tmp_details.location() == kDescriptor) break;
|
if (next_location == kField && tmp_details.location() == kDescriptor) break;
|
||||||
|
|
||||||
@ -3786,9 +3791,9 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
|||||||
if (next_kind == kAccessor &&
|
if (next_kind == kAccessor &&
|
||||||
!EqualImmutableValues(old_descriptors->GetValue(i),
|
!EqualImmutableValues(old_descriptors->GetValue(i),
|
||||||
tmp_descriptors->GetValue(i))) {
|
tmp_descriptors->GetValue(i))) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_Incompatible");
|
"GenAll_Incompatible");
|
||||||
}
|
}
|
||||||
DCHECK(!tmp_map->is_deprecated());
|
DCHECK(!tmp_map->is_deprecated());
|
||||||
target_map = tmp_map;
|
target_map = tmp_map;
|
||||||
@ -4019,9 +4024,9 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
|
|||||||
// could be inserted regardless of whether transitions array is full or not.
|
// could be inserted regardless of whether transitions array is full or not.
|
||||||
if (maybe_transition == NULL &&
|
if (maybe_transition == NULL &&
|
||||||
!TransitionArray::CanHaveMoreTransitions(split_map)) {
|
!TransitionArray::CanHaveMoreTransitions(split_map)) {
|
||||||
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
|
return CopyGeneralizeAllRepresentations(
|
||||||
new_kind, new_attributes,
|
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
|
||||||
"GenAll_CantHaveMoreTransitions");
|
"GenAll_CantHaveMoreTransitions");
|
||||||
}
|
}
|
||||||
|
|
||||||
old_map->NotifyLeafMapLayoutChange();
|
old_map->NotifyLeafMapLayoutChange();
|
||||||
@ -4102,18 +4107,27 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
|
|||||||
if (root_map == NULL) return MaybeHandle<Map>();
|
if (root_map == NULL) return MaybeHandle<Map>();
|
||||||
// From here on, use the map with correct elements kind as root map.
|
// From here on, use the map with correct elements kind as root map.
|
||||||
}
|
}
|
||||||
int root_nof = root_map->NumberOfOwnDescriptors();
|
Map* new_map = root_map->TryReplayPropertyTransitions(*old_map);
|
||||||
|
if (new_map == nullptr) return MaybeHandle<Map>();
|
||||||
|
return handle(new_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map* Map::TryReplayPropertyTransitions(Map* old_map) {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
DisallowDeoptimization no_deoptimization(GetIsolate());
|
||||||
|
|
||||||
|
int root_nof = NumberOfOwnDescriptors();
|
||||||
|
|
||||||
int old_nof = old_map->NumberOfOwnDescriptors();
|
int old_nof = old_map->NumberOfOwnDescriptors();
|
||||||
DescriptorArray* old_descriptors = old_map->instance_descriptors();
|
DescriptorArray* old_descriptors = old_map->instance_descriptors();
|
||||||
|
|
||||||
Map* new_map = root_map;
|
Map* new_map = this;
|
||||||
for (int i = root_nof; i < old_nof; ++i) {
|
for (int i = root_nof; i < old_nof; ++i) {
|
||||||
PropertyDetails old_details = old_descriptors->GetDetails(i);
|
PropertyDetails old_details = old_descriptors->GetDetails(i);
|
||||||
Map* transition = TransitionArray::SearchTransition(
|
Map* transition = TransitionArray::SearchTransition(
|
||||||
new_map, old_details.kind(), old_descriptors->GetKey(i),
|
new_map, old_details.kind(), old_descriptors->GetKey(i),
|
||||||
old_details.attributes());
|
old_details.attributes());
|
||||||
if (transition == NULL) return MaybeHandle<Map>();
|
if (transition == NULL) return nullptr;
|
||||||
new_map = transition;
|
new_map = transition;
|
||||||
DescriptorArray* new_descriptors = new_map->instance_descriptors();
|
DescriptorArray* new_descriptors = new_map->instance_descriptors();
|
||||||
|
|
||||||
@ -4121,7 +4135,7 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
|
|||||||
DCHECK_EQ(old_details.kind(), new_details.kind());
|
DCHECK_EQ(old_details.kind(), new_details.kind());
|
||||||
DCHECK_EQ(old_details.attributes(), new_details.attributes());
|
DCHECK_EQ(old_details.attributes(), new_details.attributes());
|
||||||
if (!old_details.representation().fits_into(new_details.representation())) {
|
if (!old_details.representation().fits_into(new_details.representation())) {
|
||||||
return MaybeHandle<Map>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
switch (new_details.type()) {
|
switch (new_details.type()) {
|
||||||
case DATA: {
|
case DATA: {
|
||||||
@ -4129,20 +4143,20 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
|
|||||||
// Cleared field types need special treatment. They represent lost
|
// Cleared field types need special treatment. They represent lost
|
||||||
// knowledge, so we must first generalize the new_type to "Any".
|
// knowledge, so we must first generalize the new_type to "Any".
|
||||||
if (FieldTypeIsCleared(new_details.representation(), new_type)) {
|
if (FieldTypeIsCleared(new_details.representation(), new_type)) {
|
||||||
return MaybeHandle<Map>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
PropertyType old_property_type = old_details.type();
|
PropertyType old_property_type = old_details.type();
|
||||||
if (old_property_type == DATA) {
|
if (old_property_type == DATA) {
|
||||||
FieldType* old_type = old_descriptors->GetFieldType(i);
|
FieldType* old_type = old_descriptors->GetFieldType(i);
|
||||||
if (FieldTypeIsCleared(old_details.representation(), old_type) ||
|
if (FieldTypeIsCleared(old_details.representation(), old_type) ||
|
||||||
!old_type->NowIs(new_type)) {
|
!old_type->NowIs(new_type)) {
|
||||||
return MaybeHandle<Map>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DCHECK(old_property_type == DATA_CONSTANT);
|
DCHECK(old_property_type == DATA_CONSTANT);
|
||||||
Object* old_value = old_descriptors->GetValue(i);
|
Object* old_value = old_descriptors->GetValue(i);
|
||||||
if (!new_type->NowContains(old_value)) {
|
if (!new_type->NowContains(old_value)) {
|
||||||
return MaybeHandle<Map>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -4160,14 +4174,14 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
|
|||||||
Object* old_value = old_descriptors->GetValue(i);
|
Object* old_value = old_descriptors->GetValue(i);
|
||||||
Object* new_value = new_descriptors->GetValue(i);
|
Object* new_value = new_descriptors->GetValue(i);
|
||||||
if (old_details.location() == kField || old_value != new_value) {
|
if (old_details.location() == kField || old_value != new_value) {
|
||||||
return MaybeHandle<Map>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_map->NumberOfOwnDescriptors() != old_nof) return MaybeHandle<Map>();
|
if (new_map->NumberOfOwnDescriptors() != old_nof) return nullptr;
|
||||||
return handle(new_map);
|
return new_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4760,17 +4774,28 @@ static bool ContainsMap(MapHandleList* maps, Map* map) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map* Map::FindElementsKindTransitionedMap(MapHandleList* candidates) {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
DisallowDeoptimization no_deoptimization(GetIsolate());
|
||||||
|
|
||||||
Handle<Map> Map::FindTransitionedMap(Handle<Map> map,
|
ElementsKind kind = elements_kind();
|
||||||
MapHandleList* candidates) {
|
|
||||||
ElementsKind kind = map->elements_kind();
|
|
||||||
bool packed = IsFastPackedElementsKind(kind);
|
bool packed = IsFastPackedElementsKind(kind);
|
||||||
|
|
||||||
Map* transition = nullptr;
|
Map* transition = nullptr;
|
||||||
if (IsTransitionableFastElementsKind(kind)) {
|
if (IsTransitionableFastElementsKind(kind)) {
|
||||||
for (Map* current = map->ElementsTransitionMap();
|
// Check the state of the root map.
|
||||||
current != nullptr && current->has_fast_elements();
|
Map* root_map = FindRootMap();
|
||||||
current = current->ElementsTransitionMap()) {
|
if (!EquivalentToForTransition(root_map)) return nullptr;
|
||||||
|
root_map = root_map->LookupElementsTransitionMap(kind);
|
||||||
|
DCHECK_NOT_NULL(root_map);
|
||||||
|
// Starting from the next existing elements kind transition try to
|
||||||
|
// replay the property transitions.
|
||||||
|
for (root_map = root_map->ElementsTransitionMap();
|
||||||
|
root_map != nullptr && root_map->has_fast_elements();
|
||||||
|
root_map = root_map->ElementsTransitionMap()) {
|
||||||
|
Map* current = root_map->TryReplayPropertyTransitions(this);
|
||||||
|
if (current == nullptr) continue;
|
||||||
|
|
||||||
if (ContainsMap(candidates, current) &&
|
if (ContainsMap(candidates, current) &&
|
||||||
(packed || !IsFastPackedElementsKind(current->elements_kind()))) {
|
(packed || !IsFastPackedElementsKind(current->elements_kind()))) {
|
||||||
transition = current;
|
transition = current;
|
||||||
@ -4778,11 +4803,14 @@ Handle<Map> Map::FindTransitionedMap(Handle<Map> map,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return transition == nullptr ? Handle<Map>() : handle(transition);
|
return transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
|
static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
|
||||||
|
// Ensure we are requested to search elements kind transition "near the root".
|
||||||
|
DCHECK_EQ(map->FindRootMap()->NumberOfOwnDescriptors(),
|
||||||
|
map->NumberOfOwnDescriptors());
|
||||||
Map* current_map = map;
|
Map* current_map = map;
|
||||||
|
|
||||||
ElementsKind kind = map->elements_kind();
|
ElementsKind kind = map->elements_kind();
|
||||||
@ -4911,7 +4939,7 @@ Handle<Map> Map::TransitionElementsTo(Handle<Map> map,
|
|||||||
return Map::CopyAsElementsKind(map, to_kind, OMIT_TRANSITION);
|
return Map::CopyAsElementsKind(map, to_kind, OMIT_TRANSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Map::AsElementsKind(map, to_kind);
|
return Map::ReconfigureElementsKind(map, to_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -5273,7 +5301,7 @@ void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) {
|
|||||||
} else {
|
} else {
|
||||||
TransitionElementsKind(object, to_kind);
|
TransitionElementsKind(object, to_kind);
|
||||||
}
|
}
|
||||||
map = Map::AsElementsKind(map, to_kind);
|
map = Map::ReconfigureElementsKind(map, to_kind);
|
||||||
}
|
}
|
||||||
JSObject::MigrateToMap(object, map);
|
JSObject::MigrateToMap(object, map);
|
||||||
}
|
}
|
||||||
@ -8858,6 +8886,10 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
|
|||||||
TransitionFlag flag) {
|
TransitionFlag flag) {
|
||||||
Map* maybe_elements_transition_map = NULL;
|
Map* maybe_elements_transition_map = NULL;
|
||||||
if (flag == INSERT_TRANSITION) {
|
if (flag == INSERT_TRANSITION) {
|
||||||
|
// Ensure we are requested to add elements kind transition "near the root".
|
||||||
|
DCHECK_EQ(map->FindRootMap()->NumberOfOwnDescriptors(),
|
||||||
|
map->NumberOfOwnDescriptors());
|
||||||
|
|
||||||
maybe_elements_transition_map = map->ElementsTransitionMap();
|
maybe_elements_transition_map = map->ElementsTransitionMap();
|
||||||
DCHECK(maybe_elements_transition_map == NULL ||
|
DCHECK(maybe_elements_transition_map == NULL ||
|
||||||
(maybe_elements_transition_map->elements_kind() ==
|
(maybe_elements_transition_map->elements_kind() ==
|
||||||
@ -9162,7 +9194,7 @@ Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor,
|
|||||||
// There is no benefit from reconstructing transition tree for maps without
|
// There is no benefit from reconstructing transition tree for maps without
|
||||||
// back pointers.
|
// back pointers.
|
||||||
return CopyGeneralizeAllRepresentations(
|
return CopyGeneralizeAllRepresentations(
|
||||||
map, descriptor, FORCE_FIELD, kind, attributes,
|
map, map->elements_kind(), descriptor, FORCE_FIELD, kind, attributes,
|
||||||
"GenAll_AttributesMismatchProtoMap");
|
"GenAll_AttributesMismatchProtoMap");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5769,15 +5769,14 @@ class Map: public HeapObject {
|
|||||||
static void GeneralizeFieldType(Handle<Map> map, int modify_index,
|
static void GeneralizeFieldType(Handle<Map> map, int modify_index,
|
||||||
Representation new_representation,
|
Representation new_representation,
|
||||||
Handle<FieldType> new_field_type);
|
Handle<FieldType> new_field_type);
|
||||||
static Handle<Map> ReconfigureProperty(Handle<Map> map, int modify_index,
|
|
||||||
PropertyKind new_kind,
|
static inline Handle<Map> ReconfigureProperty(
|
||||||
PropertyAttributes new_attributes,
|
Handle<Map> map, int modify_index, PropertyKind new_kind,
|
||||||
Representation new_representation,
|
PropertyAttributes new_attributes, Representation new_representation,
|
||||||
Handle<FieldType> new_field_type,
|
Handle<FieldType> new_field_type, StoreMode store_mode);
|
||||||
StoreMode store_mode);
|
|
||||||
static Handle<Map> CopyGeneralizeAllRepresentations(
|
static inline Handle<Map> ReconfigureElementsKind(
|
||||||
Handle<Map> map, int modify_index, StoreMode store_mode,
|
Handle<Map> map, ElementsKind new_elements_kind);
|
||||||
PropertyKind kind, PropertyAttributes attributes, const char* reason);
|
|
||||||
|
|
||||||
static Handle<Map> PrepareForDataProperty(Handle<Map> old_map,
|
static Handle<Map> PrepareForDataProperty(Handle<Map> old_map,
|
||||||
int descriptor_number,
|
int descriptor_number,
|
||||||
@ -5998,17 +5997,10 @@ class Map: public HeapObject {
|
|||||||
// Computes a hash value for this map, to be used in HashTables and such.
|
// Computes a hash value for this map, to be used in HashTables and such.
|
||||||
int Hash();
|
int Hash();
|
||||||
|
|
||||||
// Returns the map that this map transitions to if its elements_kind
|
|
||||||
// is changed to |elements_kind|, or NULL if no such map is cached yet.
|
|
||||||
// |safe_to_add_transitions| is set to false if adding transitions is not
|
|
||||||
// allowed.
|
|
||||||
Map* LookupElementsTransitionMap(ElementsKind elements_kind);
|
|
||||||
|
|
||||||
// Returns the transitioned map for this map with the most generic
|
// Returns the transitioned map for this map with the most generic
|
||||||
// elements_kind that's found in |candidates|, or null handle if no match is
|
// elements_kind that's found in |candidates|, or |nullptr| if no match is
|
||||||
// found at all.
|
// found at all.
|
||||||
static Handle<Map> FindTransitionedMap(Handle<Map> map,
|
Map* FindElementsKindTransitionedMap(MapHandleList* candidates);
|
||||||
MapHandleList* candidates);
|
|
||||||
|
|
||||||
inline bool CanTransition();
|
inline bool CanTransition();
|
||||||
|
|
||||||
@ -6167,6 +6159,17 @@ class Map: public HeapObject {
|
|||||||
Handle<LayoutDescriptor> full_layout_descriptor);
|
Handle<LayoutDescriptor> full_layout_descriptor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Returns the map that this (root) map transitions to if its elements_kind
|
||||||
|
// is changed to |elements_kind|, or |nullptr| if no such map is cached yet.
|
||||||
|
Map* LookupElementsTransitionMap(ElementsKind elements_kind);
|
||||||
|
|
||||||
|
// Tries to replay property transitions starting from this (root) map using
|
||||||
|
// the descriptor array of the |map|. The |root_map| is expected to have
|
||||||
|
// proper elements kind and therefore elements kinds transitions are not
|
||||||
|
// taken by this function. Returns |nullptr| if matching transition map is
|
||||||
|
// not found.
|
||||||
|
Map* TryReplayPropertyTransitions(Map* map);
|
||||||
|
|
||||||
static void ConnectTransition(Handle<Map> parent, Handle<Map> child,
|
static void ConnectTransition(Handle<Map> parent, Handle<Map> child,
|
||||||
Handle<Name> name, SimpleTransitionFlag flag);
|
Handle<Name> name, SimpleTransitionFlag flag);
|
||||||
|
|
||||||
@ -6203,6 +6206,19 @@ class Map: public HeapObject {
|
|||||||
static Handle<Map> CopyNormalized(Handle<Map> map,
|
static Handle<Map> CopyNormalized(Handle<Map> map,
|
||||||
PropertyNormalizationMode mode);
|
PropertyNormalizationMode mode);
|
||||||
|
|
||||||
|
static Handle<Map> Reconfigure(Handle<Map> map,
|
||||||
|
ElementsKind new_elements_kind,
|
||||||
|
int modify_index, PropertyKind new_kind,
|
||||||
|
PropertyAttributes new_attributes,
|
||||||
|
Representation new_representation,
|
||||||
|
Handle<FieldType> new_field_type,
|
||||||
|
StoreMode store_mode);
|
||||||
|
|
||||||
|
static Handle<Map> CopyGeneralizeAllRepresentations(
|
||||||
|
Handle<Map> map, ElementsKind elements_kind, int modify_index,
|
||||||
|
StoreMode store_mode, PropertyKind kind, PropertyAttributes attributes,
|
||||||
|
const char* reason);
|
||||||
|
|
||||||
// Fires when the layout of an object with a leaf map changes.
|
// Fires when the layout of an object with a leaf map changes.
|
||||||
// This includes adding transitions to the leaf map or changing
|
// This includes adding transitions to the leaf map or changing
|
||||||
// the descriptor array.
|
// the descriptor array.
|
||||||
|
@ -86,6 +86,7 @@ static bool EqualDetails(DescriptorArray* descriptors, int descriptor,
|
|||||||
class Expectations {
|
class Expectations {
|
||||||
static const int MAX_PROPERTIES = 10;
|
static const int MAX_PROPERTIES = 10;
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
|
ElementsKind elements_kind_;
|
||||||
PropertyType types_[MAX_PROPERTIES];
|
PropertyType types_[MAX_PROPERTIES];
|
||||||
PropertyAttributes attributes_[MAX_PROPERTIES];
|
PropertyAttributes attributes_[MAX_PROPERTIES];
|
||||||
Representation representations_[MAX_PROPERTIES];
|
Representation representations_[MAX_PROPERTIES];
|
||||||
@ -97,8 +98,15 @@ class Expectations {
|
|||||||
int number_of_properties_;
|
int number_of_properties_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit Expectations(Isolate* isolate, ElementsKind elements_kind)
|
||||||
|
: isolate_(isolate),
|
||||||
|
elements_kind_(elements_kind),
|
||||||
|
number_of_properties_(0) {}
|
||||||
|
|
||||||
explicit Expectations(Isolate* isolate)
|
explicit Expectations(Isolate* isolate)
|
||||||
: isolate_(isolate), number_of_properties_(0) {}
|
: Expectations(
|
||||||
|
isolate,
|
||||||
|
isolate->object_function()->initial_map()->elements_kind()) {}
|
||||||
|
|
||||||
void Init(int index, PropertyType type, PropertyAttributes attributes,
|
void Init(int index, PropertyType type, PropertyAttributes attributes,
|
||||||
Representation representation, Handle<Object> value) {
|
Representation representation, Handle<Object> value) {
|
||||||
@ -143,6 +151,10 @@ class Expectations {
|
|||||||
os << "\n";
|
os << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetElementsKind(ElementsKind elements_kind) {
|
||||||
|
elements_kind_ = elements_kind;
|
||||||
|
}
|
||||||
|
|
||||||
Handle<FieldType> GetFieldType(int index) {
|
Handle<FieldType> GetFieldType(int index) {
|
||||||
CHECK(index < MAX_PROPERTIES);
|
CHECK(index < MAX_PROPERTIES);
|
||||||
CHECK(types_[index] == DATA || types_[index] == ACCESSOR);
|
CHECK(types_[index] == DATA || types_[index] == ACCESSOR);
|
||||||
@ -252,6 +264,7 @@ class Expectations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Check(Map* map, int expected_nof) const {
|
bool Check(Map* map, int expected_nof) const {
|
||||||
|
CHECK_EQ(elements_kind_, map->elements_kind());
|
||||||
CHECK(number_of_properties_ <= MAX_PROPERTIES);
|
CHECK(number_of_properties_ <= MAX_PROPERTIES);
|
||||||
CHECK_EQ(expected_nof, map->NumberOfOwnDescriptors());
|
CHECK_EQ(expected_nof, map->NumberOfOwnDescriptors());
|
||||||
CHECK(!map->is_dictionary_map());
|
CHECK(!map->is_dictionary_map());
|
||||||
@ -279,6 +292,13 @@ class Expectations {
|
|||||||
// given |map|.
|
// given |map|.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
Handle<Map> AsElementsKind(Handle<Map> map, ElementsKind elements_kind) {
|
||||||
|
elements_kind_ = elements_kind;
|
||||||
|
map = Map::AsElementsKind(map, elements_kind);
|
||||||
|
CHECK_EQ(elements_kind_, map->elements_kind());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
Handle<Map> AddDataField(Handle<Map> map, PropertyAttributes attributes,
|
Handle<Map> AddDataField(Handle<Map> map, PropertyAttributes attributes,
|
||||||
Representation representation,
|
Representation representation,
|
||||||
Handle<FieldType> heap_type) {
|
Handle<FieldType> heap_type) {
|
||||||
@ -1521,6 +1541,271 @@ TEST(ReconfigureDataFieldAttribute_AccConstantToDataFieldAfterTargetMap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// A set of tests for elements kind reconfiguration case.
|
||||||
|
//
|
||||||
|
|
||||||
|
// This test ensures that representation/field type generalization is correctly
|
||||||
|
// propagated from one branch of transition tree (|map2) to another (|map|).
|
||||||
|
//
|
||||||
|
// + - p0 - p1 - p2A - p3 - p4: |map|
|
||||||
|
// |
|
||||||
|
// ek
|
||||||
|
// |
|
||||||
|
// {} - p0 - p1 - p2B - p3 - p4: |map2|
|
||||||
|
//
|
||||||
|
// where "p2A" and "p2B" differ only in the representation/field type.
|
||||||
|
//
|
||||||
|
static void TestReconfigureElementsKind_GeneralizeRepresentation(
|
||||||
|
Representation from_representation, Handle<FieldType> from_type,
|
||||||
|
Representation to_representation, Handle<FieldType> to_type,
|
||||||
|
Representation expected_representation, Handle<FieldType> expected_type) {
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
|
||||||
|
Expectations expectations(isolate, FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
// Create a map, add required properties to it and initialize expectations.
|
||||||
|
Handle<Map> initial_map = Map::Create(isolate, 0);
|
||||||
|
initial_map->set_elements_kind(FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
Handle<Map> map = initial_map;
|
||||||
|
map = expectations.AsElementsKind(map, FAST_ELEMENTS);
|
||||||
|
for (int i = 0; i < kPropCount; i++) {
|
||||||
|
map = expectations.AddDataField(map, NONE, from_representation, from_type);
|
||||||
|
}
|
||||||
|
CHECK(!map->is_deprecated());
|
||||||
|
CHECK(map->is_stable());
|
||||||
|
CHECK(expectations.Check(*map));
|
||||||
|
|
||||||
|
// Create another branch in transition tree (property at index |kDiffProp|
|
||||||
|
// has different representatio/field type), initialize expectations.
|
||||||
|
const int kDiffProp = kPropCount / 2;
|
||||||
|
Expectations expectations2(isolate, FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
Handle<Map> map2 = initial_map;
|
||||||
|
for (int i = 0; i < kPropCount; i++) {
|
||||||
|
if (i == kDiffProp) {
|
||||||
|
map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type);
|
||||||
|
} else {
|
||||||
|
map2 = expectations2.AddDataField(map2, NONE, from_representation,
|
||||||
|
from_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(!map2->is_deprecated());
|
||||||
|
CHECK(map2->is_stable());
|
||||||
|
CHECK(expectations2.Check(*map2));
|
||||||
|
|
||||||
|
Zone zone(isolate->allocator());
|
||||||
|
Handle<Map> field_owner(map->FindFieldOwner(kDiffProp), isolate);
|
||||||
|
CompilationInfo info(ArrayVector("testing"), isolate, &zone);
|
||||||
|
CHECK(!info.dependencies()->HasAborted());
|
||||||
|
info.dependencies()->AssumeFieldType(field_owner);
|
||||||
|
|
||||||
|
// Reconfigure elements kinds of |map2|, which should generalize
|
||||||
|
// representations in |map|.
|
||||||
|
Handle<Map> new_map = Map::ReconfigureElementsKind(map2, FAST_ELEMENTS);
|
||||||
|
|
||||||
|
// |map2| should be left unchanged but marked unstable.
|
||||||
|
CHECK(!map2->is_stable());
|
||||||
|
CHECK(!map2->is_deprecated());
|
||||||
|
CHECK_NE(*map2, *new_map);
|
||||||
|
CHECK(expectations2.Check(*map2));
|
||||||
|
|
||||||
|
// |map| should be deprecated and |new_map| should match new expectations.
|
||||||
|
expectations.SetDataField(kDiffProp, expected_representation, expected_type);
|
||||||
|
|
||||||
|
CHECK(map->is_deprecated());
|
||||||
|
CHECK(!info.dependencies()->HasAborted());
|
||||||
|
info.dependencies()->Rollback(); // Properly cleanup compilation info.
|
||||||
|
CHECK_NE(*map, *new_map);
|
||||||
|
|
||||||
|
CHECK(!new_map->is_deprecated());
|
||||||
|
CHECK(expectations.Check(*new_map));
|
||||||
|
|
||||||
|
// Update deprecated |map|, it should become |new_map|.
|
||||||
|
Handle<Map> updated_map = Map::Update(map);
|
||||||
|
CHECK_EQ(*new_map, *updated_map);
|
||||||
|
|
||||||
|
// Ensure Map::FindElementsKindTransitionedMap() is able to find the
|
||||||
|
// transitioned map.
|
||||||
|
{
|
||||||
|
MapHandleList map_list;
|
||||||
|
map_list.Add(updated_map);
|
||||||
|
Map* transitioned_map = map2->FindElementsKindTransitionedMap(&map_list);
|
||||||
|
CHECK_EQ(*updated_map, transitioned_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test ensures that trivial representation/field type generalization
|
||||||
|
// (from HeapObject to HeapObject) is correctly propagated from one branch of
|
||||||
|
// transition tree (|map2|) to another (|map|).
|
||||||
|
//
|
||||||
|
// + - p0 - p1 - p2A - p3 - p4: |map|
|
||||||
|
// |
|
||||||
|
// ek
|
||||||
|
// |
|
||||||
|
// {} - p0 - p1 - p2B - p3 - p4: |map2|
|
||||||
|
//
|
||||||
|
// where "p2A" and "p2B" differ only in the representation/field type.
|
||||||
|
//
|
||||||
|
static void TestReconfigureElementsKind_GeneralizeRepresentationTrivial(
|
||||||
|
Representation from_representation, Handle<FieldType> from_type,
|
||||||
|
Representation to_representation, Handle<FieldType> to_type,
|
||||||
|
Representation expected_representation, Handle<FieldType> expected_type,
|
||||||
|
bool expected_field_type_dependency = true) {
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
|
||||||
|
Expectations expectations(isolate, FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
// Create a map, add required properties to it and initialize expectations.
|
||||||
|
Handle<Map> initial_map = Map::Create(isolate, 0);
|
||||||
|
initial_map->set_elements_kind(FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
Handle<Map> map = initial_map;
|
||||||
|
map = expectations.AsElementsKind(map, FAST_ELEMENTS);
|
||||||
|
for (int i = 0; i < kPropCount; i++) {
|
||||||
|
map = expectations.AddDataField(map, NONE, from_representation, from_type);
|
||||||
|
}
|
||||||
|
CHECK(!map->is_deprecated());
|
||||||
|
CHECK(map->is_stable());
|
||||||
|
CHECK(expectations.Check(*map));
|
||||||
|
|
||||||
|
// Create another branch in transition tree (property at index |kDiffProp|
|
||||||
|
// has different attributes), initialize expectations.
|
||||||
|
const int kDiffProp = kPropCount / 2;
|
||||||
|
Expectations expectations2(isolate, FAST_SMI_ELEMENTS);
|
||||||
|
|
||||||
|
Handle<Map> map2 = initial_map;
|
||||||
|
for (int i = 0; i < kPropCount; i++) {
|
||||||
|
if (i == kDiffProp) {
|
||||||
|
map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type);
|
||||||
|
} else {
|
||||||
|
map2 = expectations2.AddDataField(map2, NONE, from_representation,
|
||||||
|
from_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(!map2->is_deprecated());
|
||||||
|
CHECK(map2->is_stable());
|
||||||
|
CHECK(expectations2.Check(*map2));
|
||||||
|
|
||||||
|
Zone zone(isolate->allocator());
|
||||||
|
Handle<Map> field_owner(map->FindFieldOwner(kDiffProp), isolate);
|
||||||
|
CompilationInfo info(ArrayVector("testing"), isolate, &zone);
|
||||||
|
CHECK(!info.dependencies()->HasAborted());
|
||||||
|
info.dependencies()->AssumeFieldType(field_owner);
|
||||||
|
|
||||||
|
// Reconfigure elements kinds of |map2|, which should generalize
|
||||||
|
// representations in |map|.
|
||||||
|
Handle<Map> new_map = Map::ReconfigureElementsKind(map2, FAST_ELEMENTS);
|
||||||
|
|
||||||
|
// |map2| should be left unchanged but marked unstable.
|
||||||
|
CHECK(!map2->is_stable());
|
||||||
|
CHECK(!map2->is_deprecated());
|
||||||
|
CHECK_NE(*map2, *new_map);
|
||||||
|
CHECK(expectations2.Check(*map2));
|
||||||
|
|
||||||
|
// In trivial case |map| should be returned as a result of the elements
|
||||||
|
// kind reconfiguration, respective field types should be generalized and
|
||||||
|
// respective code dependencies should be invalidated. |map| should be NOT
|
||||||
|
// deprecated and it should match new expectations.
|
||||||
|
expectations.SetDataField(kDiffProp, expected_representation, expected_type);
|
||||||
|
CHECK(!map->is_deprecated());
|
||||||
|
CHECK_EQ(*map, *new_map);
|
||||||
|
CHECK_EQ(expected_field_type_dependency, info.dependencies()->HasAborted());
|
||||||
|
info.dependencies()->Rollback(); // Properly cleanup compilation info.
|
||||||
|
|
||||||
|
CHECK(!new_map->is_deprecated());
|
||||||
|
CHECK(expectations.Check(*new_map));
|
||||||
|
|
||||||
|
Handle<Map> updated_map = Map::Update(map);
|
||||||
|
CHECK_EQ(*new_map, *updated_map);
|
||||||
|
|
||||||
|
// Ensure Map::FindElementsKindTransitionedMap() is able to find the
|
||||||
|
// transitioned map.
|
||||||
|
{
|
||||||
|
MapHandleList map_list;
|
||||||
|
map_list.Add(updated_map);
|
||||||
|
Map* transitioned_map = map2->FindElementsKindTransitionedMap(&map_list);
|
||||||
|
CHECK_EQ(*updated_map, transitioned_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReconfigureElementsKind_GeneralizeRepresentationSmiToDouble) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentation(
|
||||||
|
Representation::Smi(), any_type, Representation::Double(), any_type,
|
||||||
|
Representation::Double(), any_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReconfigureElementsKind_GeneralizeRepresentationSmiToTagged) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
Handle<FieldType> value_type =
|
||||||
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentation(
|
||||||
|
Representation::Smi(), any_type, Representation::HeapObject(), value_type,
|
||||||
|
Representation::Tagged(), any_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReconfigureElementsKind_GeneralizeRepresentationDoubleToTagged) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
Handle<FieldType> value_type =
|
||||||
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentation(
|
||||||
|
Representation::Double(), any_type, Representation::HeapObject(),
|
||||||
|
value_type, Representation::Tagged(), any_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReconfigureElementsKind_GeneralizeRepresentationHeapObjToHeapObj) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
|
||||||
|
Handle<FieldType> current_type =
|
||||||
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
Handle<FieldType> new_type =
|
||||||
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
Handle<FieldType> expected_type = any_type;
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentationTrivial(
|
||||||
|
Representation::HeapObject(), current_type, Representation::HeapObject(),
|
||||||
|
new_type, Representation::HeapObject(), expected_type);
|
||||||
|
current_type = expected_type;
|
||||||
|
|
||||||
|
new_type = FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentationTrivial(
|
||||||
|
Representation::HeapObject(), any_type, Representation::HeapObject(),
|
||||||
|
new_type, Representation::HeapObject(), any_type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReconfigureElementsKind_GeneralizeRepresentationHeapObjectToTagged) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
Handle<FieldType> value_type =
|
||||||
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
|
TestReconfigureElementsKind_GeneralizeRepresentation(
|
||||||
|
Representation::HeapObject(), value_type, Representation::Smi(), any_type,
|
||||||
|
Representation::Tagged(), any_type);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// A set of tests checking split map deprecation.
|
// A set of tests checking split map deprecation.
|
||||||
//
|
//
|
||||||
@ -1637,15 +1922,16 @@ static void TestGeneralizeRepresentationWithSpecialTransition(
|
|||||||
CHECK(map->is_stable());
|
CHECK(map->is_stable());
|
||||||
CHECK(expectations.Check(*map));
|
CHECK(expectations.Check(*map));
|
||||||
|
|
||||||
|
Expectations expectations2 = expectations;
|
||||||
|
|
||||||
// Apply some special transition to |map|.
|
// Apply some special transition to |map|.
|
||||||
CHECK(map->owns_descriptors());
|
CHECK(map->owns_descriptors());
|
||||||
Handle<Map> map2 = config.Transition(map);
|
Handle<Map> map2 = config.Transition(map, expectations2);
|
||||||
|
|
||||||
// |map| should still match expectations.
|
// |map| should still match expectations.
|
||||||
CHECK(!map->is_deprecated());
|
CHECK(!map->is_deprecated());
|
||||||
CHECK(expectations.Check(*map));
|
CHECK(expectations.Check(*map));
|
||||||
|
|
||||||
Expectations expectations2 = expectations;
|
|
||||||
if (config.generalizes_representations()) {
|
if (config.generalizes_representations()) {
|
||||||
for (int i = 0; i < kPropCount; i++) {
|
for (int i = 0; i < kPropCount; i++) {
|
||||||
expectations2.GeneralizeRepresentation(i);
|
expectations2.GeneralizeRepresentation(i);
|
||||||
@ -1717,13 +2003,15 @@ TEST(ElementsKindTransitionFromMapOwningDescriptor) {
|
|||||||
FieldType::Class(Map::Create(isolate, 0), isolate);
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
struct TestConfig {
|
struct TestConfig {
|
||||||
Handle<Map> Transition(Handle<Map> map) {
|
Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
|
||||||
return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS,
|
Handle<Symbol> frozen_symbol(map->GetHeap()->frozen_symbol());
|
||||||
INSERT_TRANSITION);
|
expectations.SetElementsKind(DICTIONARY_ELEMENTS);
|
||||||
|
return Map::CopyForPreventExtensions(map, NONE, frozen_symbol,
|
||||||
|
"CopyForPreventExtensions");
|
||||||
}
|
}
|
||||||
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
||||||
bool generalizes_representations() const { return false; }
|
bool generalizes_representations() const { return false; }
|
||||||
bool is_non_equevalent_transition() const { return false; }
|
bool is_non_equevalent_transition() const { return true; }
|
||||||
};
|
};
|
||||||
TestConfig config;
|
TestConfig config;
|
||||||
TestGeneralizeRepresentationWithSpecialTransition(
|
TestGeneralizeRepresentationWithSpecialTransition(
|
||||||
@ -1741,7 +2029,7 @@ TEST(ElementsKindTransitionFromMapNotOwningDescriptor) {
|
|||||||
FieldType::Class(Map::Create(isolate, 0), isolate);
|
FieldType::Class(Map::Create(isolate, 0), isolate);
|
||||||
|
|
||||||
struct TestConfig {
|
struct TestConfig {
|
||||||
Handle<Map> Transition(Handle<Map> map) {
|
Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
|
||||||
Isolate* isolate = CcTest::i_isolate();
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
Handle<FieldType> any_type = FieldType::Any(isolate);
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
|
||||||
@ -1753,12 +2041,14 @@ TEST(ElementsKindTransitionFromMapNotOwningDescriptor) {
|
|||||||
.ToHandleChecked();
|
.ToHandleChecked();
|
||||||
CHECK(!map->owns_descriptors());
|
CHECK(!map->owns_descriptors());
|
||||||
|
|
||||||
return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS,
|
Handle<Symbol> frozen_symbol(map->GetHeap()->frozen_symbol());
|
||||||
INSERT_TRANSITION);
|
expectations.SetElementsKind(DICTIONARY_ELEMENTS);
|
||||||
|
return Map::CopyForPreventExtensions(map, NONE, frozen_symbol,
|
||||||
|
"CopyForPreventExtensions");
|
||||||
}
|
}
|
||||||
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
||||||
bool generalizes_representations() const { return false; }
|
bool generalizes_representations() const { return false; }
|
||||||
bool is_non_equevalent_transition() const { return false; }
|
bool is_non_equevalent_transition() const { return true; }
|
||||||
};
|
};
|
||||||
TestConfig config;
|
TestConfig config;
|
||||||
TestGeneralizeRepresentationWithSpecialTransition(
|
TestGeneralizeRepresentationWithSpecialTransition(
|
||||||
@ -1785,7 +2075,7 @@ TEST(PrototypeTransitionFromMapOwningDescriptor) {
|
|||||||
prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
|
prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<Map> Transition(Handle<Map> map) {
|
Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
|
||||||
return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE);
|
return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE);
|
||||||
}
|
}
|
||||||
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
// TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
|
||||||
@ -1819,7 +2109,7 @@ TEST(PrototypeTransitionFromMapNotOwningDescriptor) {
|
|||||||
prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
|
prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<Map> Transition(Handle<Map> map) {
|
Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
|
||||||
Isolate* isolate = CcTest::i_isolate();
|
Isolate* isolate = CcTest::i_isolate();
|
||||||
Handle<FieldType> any_type = FieldType::Any(isolate);
|
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||||
|
|
||||||
|
61
test/mjsunit/regress/regress-v8-5009.js
Normal file
61
test/mjsunit/regress/regress-v8-5009.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
function fn1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function fn2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function fn3() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(id) {
|
||||||
|
// Just some `FunctionTemplate` to hang on
|
||||||
|
var o = new version();
|
||||||
|
|
||||||
|
o.id = id;
|
||||||
|
o[0] = null;
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setM1(o) {
|
||||||
|
o.m1 = fn1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setM2(o) {
|
||||||
|
o.m2 = fn2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAltM2(o) {
|
||||||
|
// Failing StoreIC happens here
|
||||||
|
o.m2 = fn3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAltM1(o) {
|
||||||
|
o.m1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(o) {
|
||||||
|
o.m2();
|
||||||
|
o.m1();
|
||||||
|
}
|
||||||
|
|
||||||
|
var p0 = create(0);
|
||||||
|
var p1 = create(1);
|
||||||
|
var p2 = create(2);
|
||||||
|
|
||||||
|
setM1(p0);
|
||||||
|
setM1(p1);
|
||||||
|
setM1(p2);
|
||||||
|
|
||||||
|
setM2(p0);
|
||||||
|
setAltM2(p0);
|
||||||
|
setAltM1(p0);
|
||||||
|
|
||||||
|
setAltM2(p1);
|
||||||
|
|
||||||
|
setAltM2(p2);
|
||||||
|
test(p2);
|
Loading…
Reference in New Issue
Block a user