Reland "[runtime] Add shortcuts for elements kinds transitions."
This is a reland of b90e83f5da
Original change's description:
> [runtime] Add shortcuts for elements kinds transitions.
>
> The shortcuts ensure that field type generalization is properly
> propagated in the transition graph.
>
> Bug: chromium:738763
> Change-Id: Id701a6f95ed6ea093c707fbe0bac228f1f856e9f
> Reviewed-on: https://chromium-review.googlesource.com/567992
> Commit-Queue: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#46622}
Bug: chromium:738763, chromium:742346, chromium:742381, chromium:745844
Change-Id: I93974e3906b2c7710bd525f15037a2dd97f263ad
Reviewed-on: https://chromium-review.googlesource.com/575227
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46759}
This commit is contained in:
parent
e679dd45e6
commit
6e27386d68
@ -217,6 +217,7 @@
|
||||
V(class_start_position_symbol) \
|
||||
V(detailed_stack_trace_symbol) \
|
||||
V(elements_transition_symbol) \
|
||||
V(elements_transition_shortcut_symbol) \
|
||||
V(error_end_pos_symbol) \
|
||||
V(error_script_symbol) \
|
||||
V(error_start_pos_symbol) \
|
||||
|
@ -2974,6 +2974,22 @@ void MarkCompactCollector::ClearSimpleMapTransition(Map* map,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Map* GetTransitionArrayOwnerMap(Heap* heap, TransitionArray* transitions,
|
||||
int num_transitions) {
|
||||
// Search for any non-shortcut transition.
|
||||
for (int i = 0; i < num_transitions; i++) {
|
||||
Name* key = transitions->GetKey(i);
|
||||
if (TransitionArray::IsShortcutTransition(key)) continue;
|
||||
Map* target = transitions->GetTarget(i);
|
||||
Map* parent = Map::cast(target->constructor_or_backpointer());
|
||||
return parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MarkCompactCollector::ClearFullMapTransitions() {
|
||||
HeapObject* undefined = heap()->undefined_value();
|
||||
@ -2983,16 +2999,20 @@ void MarkCompactCollector::ClearFullMapTransitions() {
|
||||
int num_transitions = array->number_of_entries();
|
||||
DCHECK_EQ(TransitionArray::NumberOfTransitions(array), num_transitions);
|
||||
if (num_transitions > 0) {
|
||||
Map* map = array->GetTarget(0);
|
||||
Map* parent = Map::cast(map->constructor_or_backpointer());
|
||||
bool parent_is_alive =
|
||||
ObjectMarking::IsBlackOrGrey(parent, MarkingState::Internal(parent));
|
||||
DescriptorArray* descriptors =
|
||||
parent_is_alive ? parent->instance_descriptors() : nullptr;
|
||||
bool descriptors_owner_died =
|
||||
CompactTransitionArray(parent, array, descriptors);
|
||||
if (descriptors_owner_died) {
|
||||
TrimDescriptorArray(parent, descriptors);
|
||||
Map* parent = GetTransitionArrayOwnerMap(heap(), array, num_transitions);
|
||||
if (!parent) {
|
||||
// The transition array contains only shortcut transitions.
|
||||
CompactTransitionArray(parent, array, nullptr);
|
||||
} else {
|
||||
bool parent_is_alive = ObjectMarking::IsBlackOrGrey(
|
||||
parent, MarkingState::Internal(parent));
|
||||
DescriptorArray* descriptors =
|
||||
parent_is_alive ? parent->instance_descriptors() : nullptr;
|
||||
bool descriptors_owner_died =
|
||||
CompactTransitionArray(parent, array, descriptors);
|
||||
if (descriptors_owner_died) {
|
||||
TrimDescriptorArray(parent, descriptors);
|
||||
}
|
||||
}
|
||||
}
|
||||
obj = array->next_link();
|
||||
@ -3001,6 +3021,41 @@ void MarkCompactCollector::ClearFullMapTransitions() {
|
||||
heap()->set_encountered_transition_arrays(Smi::kZero);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns true if the target is live and the transition entry should be kept
|
||||
// in the transition array. In addition, if current transition entry is a dead
|
||||
// shortcut transition this function tries to fixup the entry to make it point
|
||||
// to the next live target in a shortcut chain.
|
||||
bool FixupDeadTransition(bool is_shortcut_transition, Name* key, Map* target,
|
||||
TransitionArray* transitions, int transition_index) {
|
||||
if (!ObjectMarking::IsWhite(target, MarkingState::Internal(target))) {
|
||||
return true;
|
||||
}
|
||||
if (!is_shortcut_transition) {
|
||||
// Target map is dead.
|
||||
return false;
|
||||
}
|
||||
|
||||
Symbol* symbol = Symbol::cast(key);
|
||||
// Follow the shortcut transition chain in order to find live target.
|
||||
for (;;) {
|
||||
target = TransitionArray::SearchSpecial(target, symbol);
|
||||
if (target == nullptr ||
|
||||
!ObjectMarking::IsWhite(target, MarkingState::Internal(target))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
// Found live target in shortcuts chain, now fixup the transitions array.
|
||||
// Target slots do not need to be recorded since maps are not compacted.
|
||||
transitions->SetTarget(transition_index, target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool MarkCompactCollector::CompactTransitionArray(
|
||||
Map* map, TransitionArray* transitions, DescriptorArray* descriptors) {
|
||||
@ -3009,16 +3064,24 @@ bool MarkCompactCollector::CompactTransitionArray(
|
||||
int transition_index = 0;
|
||||
// Compact all live transitions to the left.
|
||||
for (int i = 0; i < num_transitions; ++i) {
|
||||
Map* target = transitions->GetTarget(i);
|
||||
DCHECK_EQ(target->constructor_or_backpointer(), map);
|
||||
if (ObjectMarking::IsWhite(target, MarkingState::Internal(target))) {
|
||||
if (descriptors != nullptr &&
|
||||
Name* key;
|
||||
Map* target;
|
||||
std::tie(key, target) = TransitionArray::GetKeyAndTarget(transitions, i);
|
||||
bool is_shortcut_transition =
|
||||
TransitionArray::IsShortcutTransition(heap(), key);
|
||||
|
||||
DCHECK_IMPLIES(!is_shortcut_transition,
|
||||
target->constructor_or_backpointer() == map);
|
||||
|
||||
bool is_live_transition = FixupDeadTransition(is_shortcut_transition, key,
|
||||
target, transitions, i);
|
||||
if (!is_live_transition) {
|
||||
if (!is_shortcut_transition && descriptors != nullptr &&
|
||||
target->instance_descriptors() == descriptors) {
|
||||
descriptors_owner_died = true;
|
||||
}
|
||||
} else {
|
||||
if (i != transition_index) {
|
||||
Name* key = transitions->GetKey(i);
|
||||
transitions->SetKey(transition_index, key);
|
||||
Object** key_slot = transitions->GetKeySlot(transition_index);
|
||||
RecordSlot(transitions, key_slot, key);
|
||||
|
@ -1438,7 +1438,7 @@ void KeyedLoadIC::LoadElementPolymorphicHandlers(
|
||||
if (receiver_map->is_stable()) {
|
||||
Map* tmap = receiver_map->FindElementsKindTransitionedMap(*receiver_maps);
|
||||
if (tmap != nullptr) {
|
||||
receiver_map->NotifyLeafMapLayoutChange();
|
||||
Map::RegisterElementsKindTransitionShortcut(receiver_map, handle(tmap));
|
||||
}
|
||||
}
|
||||
handlers->Add(LoadElementHandler(receiver_map));
|
||||
@ -2129,10 +2129,9 @@ void KeyedStoreIC::StoreElementPolymorphicHandlers(
|
||||
Map* tmap =
|
||||
receiver_map->FindElementsKindTransitionedMap(*receiver_maps);
|
||||
if (tmap != nullptr) {
|
||||
if (receiver_map->is_stable()) {
|
||||
receiver_map->NotifyLeafMapLayoutChange();
|
||||
}
|
||||
transitioned_map = handle(tmap);
|
||||
Map::RegisterElementsKindTransitionShortcut(receiver_map,
|
||||
transitioned_map);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -617,7 +617,7 @@ MapUpdater::State MapUpdater::ConstructNewMap() {
|
||||
*split_map, split_details.kind(), GetKey(split_nof),
|
||||
split_details.attributes());
|
||||
if (maybe_transition != NULL) {
|
||||
maybe_transition->DeprecateTransitionTree();
|
||||
maybe_transition->DeprecateTransitionTree(isolate_);
|
||||
}
|
||||
|
||||
// If |maybe_transition| is not NULL then the transition array already
|
||||
|
@ -1625,7 +1625,12 @@ static bool CheckOneBackPointer(Map* current_map, Object* target) {
|
||||
// static
|
||||
bool TransitionArray::IsConsistentWithBackPointers(Map* map) {
|
||||
Object* transitions = map->raw_transitions();
|
||||
Heap* heap = map->GetHeap();
|
||||
for (int i = 0; i < TransitionArray::NumberOfTransitions(transitions); ++i) {
|
||||
// Back pointers of shortcut transitions don't point to source maps.
|
||||
Name* name = TransitionArray::GetKey(transitions, i);
|
||||
if (IsShortcutTransition(heap, name)) continue;
|
||||
|
||||
Map* target = TransitionArray::GetTarget(transitions, i);
|
||||
if (!CheckOneBackPointer(map, target)) return false;
|
||||
}
|
||||
|
@ -3183,6 +3183,11 @@ Handle<Map> Map::AddMissingTransitionsForTesting(
|
||||
return AddMissingTransitions(split_map, descriptors, full_layout_descriptor);
|
||||
}
|
||||
|
||||
void Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
Isolate* isolate, Handle<Map> map, Handle<Map> transition) {
|
||||
Map::InsertElementsKindTransitionShortcut(isolate, map, transition);
|
||||
}
|
||||
|
||||
int HeapObject::SizeFromMap(Map* map) const {
|
||||
int instance_size = map->instance_size();
|
||||
if (instance_size != kVariableSizeSentinel) return instance_size;
|
||||
|
@ -1706,6 +1706,9 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
|
||||
} else if (key == heap->elements_transition_symbol()) {
|
||||
os << "(transition to " << ElementsKindToString(target->elements_kind())
|
||||
<< ")";
|
||||
} else if (key == heap->elements_transition_shortcut_symbol()) {
|
||||
os << "(shortcut to " << ElementsKindToString(target->elements_kind())
|
||||
<< ")";
|
||||
} else if (key == heap->strict_function_transition_symbol()) {
|
||||
os << " (transition to strict function)";
|
||||
} else {
|
||||
@ -1737,8 +1740,7 @@ void TransitionArray::PrintTransitionTree(std::ostream& os, Map* map,
|
||||
for (int i = 0; i < num_transitions; i++) {
|
||||
Name* key = GetKey(transitions, i);
|
||||
Map* target = GetTarget(transitions, i);
|
||||
os << std::endl
|
||||
<< " " << level << "/" << i << ":" << std::setw(level * 2 + 2) << " ";
|
||||
os << "\n " << level << "/" << i << ":" << std::setw(level * 2 + 2) << " ";
|
||||
std::stringstream ss;
|
||||
ss << Brief(target);
|
||||
os << std::left << std::setw(50) << ss.str() << ": ";
|
||||
@ -1752,17 +1754,21 @@ void TransitionArray::PrintTransitionTree(std::ostream& os, Map* map,
|
||||
os << "to frozen";
|
||||
} else if (key == heap->elements_transition_symbol()) {
|
||||
os << "to " << ElementsKindToString(target->elements_kind());
|
||||
} else if (key == heap->elements_transition_shortcut_symbol()) {
|
||||
os << "shortcut to " << ElementsKindToString(target->elements_kind());
|
||||
// Don't print transitions "through" the transition shortcut.
|
||||
continue;
|
||||
} else if (key == heap->strict_function_transition_symbol()) {
|
||||
os << "to strict function";
|
||||
} else {
|
||||
DCHECK(!IsSpecialTransition(key));
|
||||
os << "to ";
|
||||
#ifdef OBJECT_PRINT
|
||||
key->NamePrint(os);
|
||||
#else
|
||||
key->ShortPrint(os);
|
||||
#endif
|
||||
os << " ";
|
||||
DCHECK(!IsSpecialTransition(key));
|
||||
os << "to ";
|
||||
int descriptor = target->LastAdded();
|
||||
DescriptorArray* descriptors = target->instance_descriptors();
|
||||
descriptors->PrintDescriptorDetails(os, descriptor,
|
||||
|
295
src/objects.cc
295
src/objects.cc
@ -4246,13 +4246,20 @@ Handle<Map> Map::CopyGeneralizeAllFields(Handle<Map> map,
|
||||
return new_map;
|
||||
}
|
||||
|
||||
|
||||
void Map::DeprecateTransitionTree() {
|
||||
void Map::DeprecateTransitionTree(Isolate* isolate) {
|
||||
DisallowHeapAllocation no_allocation;
|
||||
if (is_deprecated()) return;
|
||||
Heap* heap = isolate->heap();
|
||||
Object* transitions = raw_transitions();
|
||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
||||
for (int i = 0; i < num_transitions; ++i) {
|
||||
TransitionArray::GetTarget(transitions, i)->DeprecateTransitionTree();
|
||||
Name* key;
|
||||
Map* target;
|
||||
std::tie(key, target) = TransitionArray::GetKeyAndTarget(transitions, i);
|
||||
// Don't follow transition shortcuts during deprecation, otherwise
|
||||
// we will deprecate unrelated maps.
|
||||
if (TransitionArray::IsShortcutTransition(heap, key)) continue;
|
||||
target->DeprecateTransitionTree(isolate);
|
||||
}
|
||||
DCHECK(!constructor_or_backpointer()->IsFunctionTemplateInfo());
|
||||
deprecate();
|
||||
@ -4320,18 +4327,20 @@ Map* Map::FindFieldOwner(int descriptor) const {
|
||||
return const_cast<Map*>(result);
|
||||
}
|
||||
|
||||
void Map::UpdateFieldType(int descriptor, Handle<Name> name,
|
||||
bool Map::UpdateFieldType(int descriptor, Handle<Name> name,
|
||||
PropertyConstness new_constness,
|
||||
Representation new_representation,
|
||||
Handle<Object> new_wrapped_type) {
|
||||
bool has_transition_shortcut = false;
|
||||
DCHECK(new_wrapped_type->IsSmi() || new_wrapped_type->IsWeakCell());
|
||||
// We store raw pointers in the queue, so no allocations are allowed.
|
||||
DisallowHeapAllocation no_allocation;
|
||||
PropertyDetails details = instance_descriptors()->GetDetails(descriptor);
|
||||
if (details.location() != kField) return;
|
||||
if (details.location() != kField) return has_transition_shortcut;
|
||||
DCHECK_EQ(kData, details.kind());
|
||||
|
||||
Zone zone(GetIsolate()->allocator(), ZONE_NAME);
|
||||
Heap* heap = GetHeap();
|
||||
Isolate* isolate = heap->isolate();
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
ZoneQueue<Map*> backlog(&zone);
|
||||
backlog.push(this);
|
||||
|
||||
@ -4342,7 +4351,15 @@ void Map::UpdateFieldType(int descriptor, Handle<Name> name,
|
||||
Object* transitions = current->raw_transitions();
|
||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
||||
for (int i = 0; i < num_transitions; ++i) {
|
||||
Map* target = TransitionArray::GetTarget(transitions, i);
|
||||
Name* key;
|
||||
Map* target;
|
||||
std::tie(key, target) = TransitionArray::GetKeyAndTarget(transitions, i);
|
||||
if (TransitionArray::IsShortcutTransition(heap, key)) {
|
||||
// Handling of transition shortcuts may also require deoptimization of
|
||||
// dependent code, Map::GeneralizeField() takes care of this case.
|
||||
has_transition_shortcut = true;
|
||||
continue;
|
||||
}
|
||||
backlog.push(target);
|
||||
}
|
||||
DescriptorArray* descriptors = current->instance_descriptors();
|
||||
@ -4366,6 +4383,7 @@ void Map::UpdateFieldType(int descriptor, Handle<Name> name,
|
||||
descriptors->Replace(descriptor, &d);
|
||||
}
|
||||
}
|
||||
return has_transition_shortcut;
|
||||
}
|
||||
|
||||
bool FieldTypeIsCleared(Representation rep, FieldType* type) {
|
||||
@ -4390,7 +4408,6 @@ Handle<FieldType> Map::GeneralizeFieldType(Representation rep1,
|
||||
return FieldType::Any(isolate);
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void Map::GeneralizeField(Handle<Map> map, int modify_index,
|
||||
PropertyConstness new_constness,
|
||||
@ -4398,58 +4415,73 @@ void Map::GeneralizeField(Handle<Map> map, int modify_index,
|
||||
Handle<FieldType> new_field_type) {
|
||||
Isolate* isolate = map->GetIsolate();
|
||||
|
||||
// Check if we actually need to generalize the field type at all.
|
||||
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate);
|
||||
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
||||
PropertyConstness old_constness = old_details.constness();
|
||||
Representation old_representation = old_details.representation();
|
||||
Handle<FieldType> old_field_type(old_descriptors->GetFieldType(modify_index),
|
||||
isolate);
|
||||
for (;;) {
|
||||
// Check if we actually need to generalize the field type at all.
|
||||
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(),
|
||||
isolate);
|
||||
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
|
||||
PropertyConstness old_constness = old_details.constness();
|
||||
Representation old_representation = old_details.representation();
|
||||
Handle<FieldType> old_field_type(
|
||||
old_descriptors->GetFieldType(modify_index), isolate);
|
||||
|
||||
// Return if the current map is general enough to hold requested contness and
|
||||
// representation/field type.
|
||||
if (((FLAG_modify_map_inplace &&
|
||||
IsGeneralizableTo(new_constness, old_constness)) ||
|
||||
(!FLAG_modify_map_inplace && (old_constness == new_constness))) &&
|
||||
old_representation.Equals(new_representation) &&
|
||||
!FieldTypeIsCleared(new_representation, *new_field_type) &&
|
||||
// Checking old_field_type for being cleared is not necessary because
|
||||
// the NowIs check below would fail anyway in that case.
|
||||
new_field_type->NowIs(old_field_type)) {
|
||||
DCHECK(GeneralizeFieldType(old_representation, old_field_type,
|
||||
new_representation, new_field_type, isolate)
|
||||
->NowIs(old_field_type));
|
||||
return;
|
||||
}
|
||||
// Return if the current map is general enough to hold requested constness
|
||||
// and representation/field type.
|
||||
if (((FLAG_modify_map_inplace &&
|
||||
IsGeneralizableTo(new_constness, old_constness)) ||
|
||||
(!FLAG_modify_map_inplace && (old_constness == new_constness))) &&
|
||||
old_representation.Equals(new_representation) &&
|
||||
!FieldTypeIsCleared(new_representation, *new_field_type) &&
|
||||
// Checking old_field_type for being cleared is not necessary because
|
||||
// the NowIs check below would fail anyway in that case.
|
||||
new_field_type->NowIs(old_field_type)) {
|
||||
DCHECK(GeneralizeFieldType(old_representation, old_field_type,
|
||||
new_representation, new_field_type, isolate)
|
||||
->NowIs(old_field_type));
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the field owner.
|
||||
Handle<Map> field_owner(map->FindFieldOwner(modify_index), isolate);
|
||||
Handle<DescriptorArray> descriptors(
|
||||
field_owner->instance_descriptors(), isolate);
|
||||
DCHECK_EQ(*old_field_type, descriptors->GetFieldType(modify_index));
|
||||
// Determine the field owner.
|
||||
Handle<Map> field_owner(map->FindFieldOwner(modify_index), isolate);
|
||||
Handle<DescriptorArray> descriptors(field_owner->instance_descriptors(),
|
||||
isolate);
|
||||
DCHECK_EQ(*old_field_type, descriptors->GetFieldType(modify_index));
|
||||
|
||||
new_field_type =
|
||||
Map::GeneralizeFieldType(old_representation, old_field_type,
|
||||
new_representation, new_field_type, isolate);
|
||||
if (FLAG_modify_map_inplace) {
|
||||
new_constness = GeneralizeConstness(old_constness, new_constness);
|
||||
}
|
||||
new_field_type =
|
||||
Map::GeneralizeFieldType(old_representation, old_field_type,
|
||||
new_representation, new_field_type, isolate);
|
||||
if (FLAG_modify_map_inplace) {
|
||||
new_constness = GeneralizeConstness(old_constness, new_constness);
|
||||
}
|
||||
|
||||
PropertyDetails details = descriptors->GetDetails(modify_index);
|
||||
Handle<Name> name(descriptors->GetKey(modify_index));
|
||||
PropertyDetails details = descriptors->GetDetails(modify_index);
|
||||
Handle<Name> name(descriptors->GetKey(modify_index));
|
||||
|
||||
Handle<Object> wrapped_type(WrapFieldType(new_field_type));
|
||||
field_owner->UpdateFieldType(modify_index, name, new_constness,
|
||||
new_representation, wrapped_type);
|
||||
field_owner->dependent_code()->DeoptimizeDependentCodeGroup(
|
||||
isolate, DependentCode::kFieldOwnerGroup);
|
||||
Handle<Object> wrapped_type(WrapFieldType(new_field_type));
|
||||
bool has_elements_transition_shortcut = field_owner->UpdateFieldType(
|
||||
modify_index, name, new_constness, new_representation, wrapped_type);
|
||||
field_owner->dependent_code()->DeoptimizeDependentCodeGroup(
|
||||
isolate, DependentCode::kFieldOwnerGroup);
|
||||
|
||||
if (FLAG_trace_generalization) {
|
||||
map->PrintGeneralization(
|
||||
stdout, "field type generalization", modify_index,
|
||||
map->NumberOfOwnDescriptors(), map->NumberOfOwnDescriptors(), false,
|
||||
details.representation(), details.representation(), old_field_type,
|
||||
MaybeHandle<Object>(), new_field_type, MaybeHandle<Object>());
|
||||
if (FLAG_trace_generalization) {
|
||||
map->PrintGeneralization(
|
||||
stdout, "field type generalization", modify_index,
|
||||
map->NumberOfOwnDescriptors(), map->NumberOfOwnDescriptors(), false,
|
||||
details.representation(), details.representation(), old_field_type,
|
||||
MaybeHandle<Object>(), new_field_type, MaybeHandle<Object>());
|
||||
}
|
||||
|
||||
if (!has_elements_transition_shortcut) {
|
||||
// Nothing else to be done here.
|
||||
break;
|
||||
}
|
||||
|
||||
// Repeat generalization procedure for the target of elements kind
|
||||
// transition shortcut and updated |new_field_type| and |new_constness|.
|
||||
Map* transition = TransitionArray::SearchSpecial(
|
||||
*field_owner, isolate->heap()->elements_transition_shortcut_symbol());
|
||||
DCHECK(transition);
|
||||
map = handle(transition, isolate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4459,14 +4491,14 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> map, int modify_index,
|
||||
PropertyKind new_kind,
|
||||
PropertyAttributes new_attributes,
|
||||
Representation new_representation,
|
||||
Handle<FieldType> new_field_type) {
|
||||
Handle<FieldType> new_field_type,
|
||||
PropertyConstness new_constness) {
|
||||
DCHECK_EQ(kData, new_kind); // Only kData case is supported.
|
||||
MapUpdater mu(map->GetIsolate(), map);
|
||||
return mu.ReconfigureToDataField(modify_index, new_attributes, kConst,
|
||||
return mu.ReconfigureToDataField(modify_index, new_attributes, new_constness,
|
||||
new_representation, new_field_type);
|
||||
}
|
||||
|
||||
// TODO(ishell): remove.
|
||||
// static
|
||||
Handle<Map> Map::ReconfigureElementsKind(Handle<Map> map,
|
||||
ElementsKind new_elements_kind) {
|
||||
@ -9190,6 +9222,151 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
|
||||
return new_map;
|
||||
}
|
||||
|
||||
// Searches for next non-deprecated map in a transition shortcuts chain,
|
||||
// removes deprecated maps on the way. If all the maps in a chain after |map|
|
||||
// are deprecated then they are not removed (because in such a case the caller
|
||||
// code will replace the shortcut transition from |map| anyway).
|
||||
MaybeHandle<Map> Map::GetTransitionShortcutRemoveDeprecated(
|
||||
Isolate* isolate, Handle<Map> map, Handle<Symbol> name) {
|
||||
bool seen_deprecated_maps = false;
|
||||
Map* maybe_transition = *map;
|
||||
for (;;) {
|
||||
maybe_transition = TransitionArray::SearchSpecial(maybe_transition, *name);
|
||||
if (maybe_transition == NULL) return MaybeHandle<Map>();
|
||||
if (!maybe_transition->is_deprecated()) break;
|
||||
seen_deprecated_maps = true;
|
||||
}
|
||||
Handle<Map> transition(maybe_transition, isolate);
|
||||
if (seen_deprecated_maps) {
|
||||
// Remove deprecated maps from the chain.
|
||||
Map::ConnectTransition(map, transition, name, SPECIAL_SHORTCUT_TRANSITION);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
// Find the right place for the elements kind transition shortcut in the chain
|
||||
// of elements kind transitions shortcuts starting from |map|.
|
||||
void Map::InsertElementsKindTransitionShortcut(Isolate* isolate,
|
||||
Handle<Map> map,
|
||||
Handle<Map> transition) {
|
||||
DCHECK_NE(*map, *transition);
|
||||
DCHECK(IsMoreGeneralElementsKindTransition(map->elements_kind(),
|
||||
transition->elements_kind()));
|
||||
DCHECK(!map->is_deprecated());
|
||||
DCHECK(!transition->is_deprecated());
|
||||
|
||||
Handle<Symbol> name =
|
||||
isolate->factory()->elements_transition_shortcut_symbol();
|
||||
|
||||
for (;;) {
|
||||
// We don't need to keep deprecated maps in the shortcuts chain because
|
||||
// there can be an elements kind transition generated neither from
|
||||
// the deprecated map nor from the updated map. In the former case ICs will
|
||||
// just miss on non-deprecated map check. In the latter case the deprecation
|
||||
// means that the map has changed "incompatibly".
|
||||
Handle<Map> next_map;
|
||||
if (!GetTransitionShortcutRemoveDeprecated(isolate, map, name)
|
||||
.ToHandle(&next_map)) {
|
||||
// If we reach the end of elements kind transition chain then this is
|
||||
// point where we are going to add the shortcut |map|->|transition|.
|
||||
break;
|
||||
}
|
||||
// If shortcut to the |transition| is already added then we are done.
|
||||
if (*next_map == *transition) return;
|
||||
|
||||
DCHECK(!next_map->is_deprecated());
|
||||
DCHECK_NE(next_map->elements_kind(), transition->elements_kind());
|
||||
if (!IsMoreGeneralElementsKindTransition(next_map->elements_kind(),
|
||||
transition->elements_kind())) {
|
||||
// |next_map| has more general elements kind than |transition|, so we
|
||||
// append |transition| that may already have a non empty tail to |map|
|
||||
// and repeat the whole procedure to insert a shortcut from
|
||||
// |transition| to |next_map| that may already have a non empty tail.
|
||||
// This way we ensure that both |next_map|'s tail and |transition|'s tail
|
||||
// are correctly merged together.
|
||||
Map::ConnectTransition(map, transition, name,
|
||||
SPECIAL_SHORTCUT_TRANSITION);
|
||||
map = transition;
|
||||
transition = next_map;
|
||||
continue;
|
||||
}
|
||||
map = next_map;
|
||||
}
|
||||
Map::ConnectTransition(map, transition, name, SPECIAL_SHORTCUT_TRANSITION);
|
||||
}
|
||||
|
||||
// Register elements kind transition shortcuts from |map| to |transition|.
|
||||
// When polymorphic keyed IC has maps that differs only in elements kind then
|
||||
// we may generate an IC handler or optimized code that will perform elements
|
||||
// kind transition to most general elements kind among the maps recorded by
|
||||
// the IC. In a case when maps have fields that can be generalized inplace
|
||||
// (for example, when the field has HeapObject representation with non-any
|
||||
// field type) we have to ensure that the generalization is properly propagated
|
||||
// from source map of elements kind transition to the target map.
|
||||
// Otherwise we may end up having an object with a field value that does not
|
||||
// match the field type after elements kind transition.
|
||||
// Consider the following example:
|
||||
//
|
||||
// + - p0 - p1 - ... - pN: |transition|
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// ek { ek shortcuts }
|
||||
// | | | |
|
||||
// {} - p0 - p1 - ... - pN: |map|
|
||||
//
|
||||
// The function inserts the shortcut links from every field owner map with
|
||||
// source elements kind to respective field owner map with target elements kind.
|
||||
// The shortcut links are treated differently during transition DAG traversal:
|
||||
// - We don't deprecate maps through shortcut links,
|
||||
// - When we generalize a field we follow only that field's shortcut link.
|
||||
// The elements kind transition shortcuts are chained the same way we chain
|
||||
// ordinary elements kind transitions (from less general to more general
|
||||
// elements kinds).
|
||||
void Map::RegisterElementsKindTransitionShortcut(Handle<Map> map,
|
||||
Handle<Map> transition) {
|
||||
DCHECK_NE(*map, *transition);
|
||||
DCHECK(map->EquivalentToForTransition(*transition));
|
||||
DCHECK(IsMoreGeneralElementsKindTransition(map->elements_kind(),
|
||||
transition->elements_kind()));
|
||||
#ifdef ENABLE_SLOW_DCHECKS
|
||||
if (FLAG_enable_slow_asserts) {
|
||||
// Ensure that all fields in |transition| are compatible with fields
|
||||
// in |map|. Otherwise it's incorrect to register such an elements kind
|
||||
// transition.
|
||||
MapHandles map_list;
|
||||
map_list.push_back(transition);
|
||||
Map* transitioned_map = map->FindElementsKindTransitionedMap(map_list);
|
||||
CHECK_EQ(*transition, transitioned_map);
|
||||
}
|
||||
#endif
|
||||
|
||||
int root_nof;
|
||||
{
|
||||
Map* root_map = map->FindRootMap();
|
||||
root_nof = root_map->NumberOfOwnDescriptors();
|
||||
if (map->NumberOfOwnDescriptors() == root_nof) {
|
||||
// If there were no descriptors added from the root map then the shortcut
|
||||
// transition is not needed because at this position we will insert
|
||||
// regular elements kind transitions.
|
||||
// At this point the map must have already been marked as unstable.
|
||||
DCHECK(!map->is_stable());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Isolate* isolate = map->GetIsolate();
|
||||
while (map->NumberOfOwnDescriptors() != root_nof) {
|
||||
map->NotifyLeafMapLayoutChange();
|
||||
// TODO(ishell): insert shortcut only when there's anything to propagate
|
||||
// from |map| to |transition| (when |transition|'s last descriptor is
|
||||
// a constant field or a field with non-"any" field type).
|
||||
InsertElementsKindTransitionShortcut(isolate, map, transition);
|
||||
|
||||
map = handle(Map::cast(map->GetBackPointer()), isolate);
|
||||
transition = handle(Map::cast(transition->GetBackPointer()), isolate);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map,
|
||||
Handle<SharedFunctionInfo> shared_info) {
|
||||
DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type());
|
||||
@ -9451,7 +9628,7 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
|
||||
constructor->context()->native_context()->object_function());
|
||||
Handle<Map> initial_map(constructor->initial_map(), isolate);
|
||||
result = Map::Normalize(initial_map, CLEAR_INOBJECT_PROPERTIES, reason);
|
||||
initial_map->DeprecateTransitionTree();
|
||||
initial_map->DeprecateTransitionTree(isolate);
|
||||
Handle<Object> prototype(result->prototype(), isolate);
|
||||
JSFunction::SetInitialMap(constructor, result, prototype);
|
||||
|
||||
|
@ -249,10 +249,11 @@ enum TransitionFlag {
|
||||
enum SimpleTransitionFlag {
|
||||
SIMPLE_PROPERTY_TRANSITION,
|
||||
PROPERTY_TRANSITION,
|
||||
SPECIAL_TRANSITION
|
||||
// Below are the special transitions.
|
||||
SPECIAL_TRANSITION,
|
||||
SPECIAL_SHORTCUT_TRANSITION
|
||||
};
|
||||
|
||||
|
||||
// Indicates whether we are only interested in the descriptors of a particular
|
||||
// map, or in all descriptors in the descriptor array.
|
||||
enum DescriptorFlag {
|
||||
|
@ -345,11 +345,11 @@ class Map : public HeapObject {
|
||||
Representation new_representation,
|
||||
Handle<FieldType> new_field_type);
|
||||
|
||||
static Handle<Map> ReconfigureProperty(Handle<Map> map, int modify_index,
|
||||
PropertyKind new_kind,
|
||||
PropertyAttributes new_attributes,
|
||||
Representation new_representation,
|
||||
Handle<FieldType> new_field_type);
|
||||
static Handle<Map> ReconfigureProperty(
|
||||
Handle<Map> map, int modify_index, PropertyKind new_kind,
|
||||
PropertyAttributes new_attributes, Representation new_representation,
|
||||
Handle<FieldType> new_field_type,
|
||||
PropertyConstness new_constness = kConst);
|
||||
|
||||
static Handle<Map> ReconfigureElementsKind(Handle<Map> map,
|
||||
ElementsKind new_elements_kind);
|
||||
@ -495,6 +495,9 @@ class Map : public HeapObject {
|
||||
static Handle<Map> TransitionElementsTo(Handle<Map> map,
|
||||
ElementsKind to_kind);
|
||||
|
||||
static void RegisterElementsKindTransitionShortcut(Handle<Map> map,
|
||||
Handle<Map> transition);
|
||||
|
||||
static Handle<Map> AsElementsKind(Handle<Map> map, ElementsKind kind);
|
||||
|
||||
static Handle<Map> CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
|
||||
@ -744,6 +747,9 @@ class Map : public HeapObject {
|
||||
Handle<Map> split_map, Handle<DescriptorArray> descriptors,
|
||||
Handle<LayoutDescriptor> full_layout_descriptor);
|
||||
|
||||
static inline void InsertElementsKindTransitionShortcutForTesting(
|
||||
Isolate* isolate, Handle<Map> map, Handle<Map> transition);
|
||||
|
||||
// Fires when the layout of an object with a leaf map changes.
|
||||
// This includes adding transitions to the leaf map or changing
|
||||
// the descriptor array.
|
||||
@ -763,6 +769,12 @@ class Map : public HeapObject {
|
||||
// not found.
|
||||
Map* TryReplayPropertyTransitions(Map* map);
|
||||
|
||||
static MaybeHandle<Map> GetTransitionShortcutRemoveDeprecated(
|
||||
Isolate* isolate, Handle<Map> map, Handle<Symbol> name);
|
||||
static void InsertElementsKindTransitionShortcut(Isolate* isolate,
|
||||
Handle<Map> map,
|
||||
Handle<Map> transition);
|
||||
|
||||
static void ConnectTransition(Handle<Map> parent, Handle<Map> child,
|
||||
Handle<Name> name, SimpleTransitionFlag flag);
|
||||
|
||||
@ -802,7 +814,7 @@ class Map : public HeapObject {
|
||||
Handle<Map> map, ElementsKind elements_kind, int modify_index,
|
||||
PropertyKind kind, PropertyAttributes attributes, const char* reason);
|
||||
|
||||
void DeprecateTransitionTree();
|
||||
void DeprecateTransitionTree(Isolate* isolate);
|
||||
|
||||
void ReplaceDescriptors(DescriptorArray* new_descriptors,
|
||||
LayoutDescriptor* new_layout_descriptor);
|
||||
@ -810,7 +822,8 @@ class Map : public HeapObject {
|
||||
// Update field type of the given descriptor to new representation and new
|
||||
// type. The type must be prepared for storing in descriptor array:
|
||||
// it must be either a simple type or a map wrapped in a weak cell.
|
||||
void UpdateFieldType(int descriptor_number, Handle<Name> name,
|
||||
// Returns true if the elements kind transition shortcut exists.
|
||||
bool UpdateFieldType(int descriptor_number, Handle<Name> name,
|
||||
PropertyConstness new_constness,
|
||||
Representation new_representation,
|
||||
Handle<Object> new_wrapped_type);
|
||||
|
@ -98,6 +98,18 @@ void TransitionArray::SetTarget(int transition_number, Map* value) {
|
||||
set(ToTargetIndex(transition_number), value);
|
||||
}
|
||||
|
||||
std::pair<Name*, Map*> TransitionArray::GetKeyAndTarget(Object* raw_transitions,
|
||||
int transition_number) {
|
||||
if (IsSimpleTransition(raw_transitions)) {
|
||||
DCHECK(transition_number == 0);
|
||||
Map* transition = GetSimpleTransition(raw_transitions);
|
||||
return std::make_pair(GetSimpleTransitionKey(transition), transition);
|
||||
}
|
||||
DCHECK(IsFullTransitionArray(raw_transitions));
|
||||
TransitionArray* transition_array = TransitionArray::cast(raw_transitions);
|
||||
return std::make_pair(transition_array->GetKey(transition_number),
|
||||
transition_array->GetTarget(transition_number));
|
||||
}
|
||||
|
||||
int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
|
||||
DCHECK(name->IsUniqueName());
|
||||
@ -112,9 +124,17 @@ bool TransitionArray::IsSpecialTransition(Name* name) {
|
||||
return name == heap->nonextensible_symbol() ||
|
||||
name == heap->sealed_symbol() || name == heap->frozen_symbol() ||
|
||||
name == heap->elements_transition_symbol() ||
|
||||
name == heap->elements_transition_shortcut_symbol() ||
|
||||
name == heap->strict_function_transition_symbol();
|
||||
}
|
||||
|
||||
bool TransitionArray::IsShortcutTransition(Name* name) {
|
||||
return IsShortcutTransition(name->GetHeap(), name);
|
||||
}
|
||||
|
||||
bool TransitionArray::IsShortcutTransition(Heap* heap, Name* name) {
|
||||
return name == heap->elements_transition_shortcut_symbol();
|
||||
}
|
||||
|
||||
int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
||||
PropertyAttributes attributes1, Name* key2,
|
||||
|
@ -16,7 +16,9 @@ namespace internal {
|
||||
void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
||||
Handle<Map> target, SimpleTransitionFlag flag) {
|
||||
Isolate* isolate = map->GetIsolate();
|
||||
target->SetBackPointer(*map);
|
||||
if (flag != SPECIAL_SHORTCUT_TRANSITION) {
|
||||
target->SetBackPointer(*map);
|
||||
}
|
||||
|
||||
// If the map doesn't have any transitions at all yet, install the new one.
|
||||
if (CanStoreSimpleTransition(map->raw_transitions())) {
|
||||
@ -30,7 +32,7 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
||||
ReplaceTransitions(map, *result);
|
||||
}
|
||||
|
||||
bool is_special_transition = flag == SPECIAL_TRANSITION;
|
||||
bool is_special_transition = flag >= SPECIAL_TRANSITION;
|
||||
// If the map has a simple transition, check if it should be overwritten.
|
||||
if (IsSimpleTransition(map->raw_transitions())) {
|
||||
Map* old_target = GetSimpleTransition(map->raw_transitions());
|
||||
|
@ -153,6 +153,9 @@ class TransitionArray: public FixedArray {
|
||||
inline Map* GetTarget(int transition_number);
|
||||
inline void SetTarget(int transition_number, Map* target);
|
||||
|
||||
static inline std::pair<Name*, Map*> GetKeyAndTarget(Object* raw_transitions,
|
||||
int transition_number);
|
||||
|
||||
static inline PropertyDetails GetTargetDetails(Name* name, Map* target);
|
||||
|
||||
// Returns the number of transitions in the array.
|
||||
@ -204,6 +207,10 @@ class TransitionArray: public FixedArray {
|
||||
// or frozen transitions.
|
||||
static inline bool IsSpecialTransition(Name* name);
|
||||
|
||||
// Returns true for shortcut transitions.
|
||||
static inline bool IsShortcutTransition(Name* name);
|
||||
static inline bool IsShortcutTransition(Heap* heap, Name* name);
|
||||
|
||||
// Constant for denoting key was not found.
|
||||
static const int kNotFound = -1;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/compilation-cache.h"
|
||||
#include "src/compilation-dependencies.h"
|
||||
#include "src/compilation-info.h"
|
||||
@ -1868,6 +1869,107 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial(
|
||||
}
|
||||
}
|
||||
|
||||
// This test ensures that trivial field generalization (from HeapObject to
|
||||
// HeapObject) is correctly propagated from one branch of transition tree
|
||||
// (|map2|) to another (|map|) also through transition shortcuts.
|
||||
//
|
||||
// + - p0 - p1 - p2A - p3 - p4: |map|
|
||||
// ^ ^ ^ ^ ^ ^
|
||||
// | | | | | |
|
||||
// ek { ek shortcuts }
|
||||
// | | | | | |
|
||||
// {} - p0 - p1 - p2B - p3 - p4: |map2|
|
||||
//
|
||||
// where "p2A" and "p2B" differ only in the representation/field type.
|
||||
//
|
||||
static void TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
const CRFTData& from, const CRFTData& to, const CRFTData& shortcut,
|
||||
const CRFTData& expected, bool expected_field_type_dependency = true) {
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
|
||||
Expectations expectations(isolate, PACKED_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(PACKED_SMI_ELEMENTS);
|
||||
|
||||
Handle<Map> map = initial_map;
|
||||
map = expectations.AsElementsKind(map, PACKED_ELEMENTS);
|
||||
for (int i = 0; i < kPropCount; i++) {
|
||||
// Add a branch in transition tree to prevent descriptors sharing.
|
||||
Handle<String> name = MakeName("tmp", i);
|
||||
Map::CopyWithField(map, name, shortcut.type, NONE, shortcut.constness,
|
||||
shortcut.representation, INSERT_TRANSITION)
|
||||
.ToHandleChecked();
|
||||
|
||||
map = expectations.AddDataField(map, NONE, shortcut.constness,
|
||||
shortcut.representation, shortcut.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.
|
||||
Expectations expectations2(isolate, PACKED_SMI_ELEMENTS);
|
||||
|
||||
Handle<Map> map2 = initial_map;
|
||||
for (int i = 0; i < kPropCount; i++) {
|
||||
map2 = expectations2.AddDataField(map2, NONE, from.constness,
|
||||
from.representation, from.type);
|
||||
}
|
||||
CHECK(!map2->is_deprecated());
|
||||
CHECK(map2->is_stable());
|
||||
CHECK(expectations2.Check(*map2));
|
||||
|
||||
// Register elements kind transition shortcut.
|
||||
Map::RegisterElementsKindTransitionShortcut(map2, map);
|
||||
CHECK(!map2->is_stable());
|
||||
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
{
|
||||
const int kDiffProp = kPropCount / 2;
|
||||
Handle<Map> field_owner(map->FindFieldOwner(kDiffProp), isolate);
|
||||
CompilationDependencies dependencies(isolate, &zone);
|
||||
CHECK(!dependencies.HasAborted());
|
||||
dependencies.AssumeFieldOwner(field_owner);
|
||||
|
||||
// Reconfigure property of |map2|, which should generalize representations
|
||||
// in |map| through the elements kind shortcut.
|
||||
Handle<Map> new_map = Map::ReconfigureProperty(
|
||||
map2, kDiffProp, kData, NONE, to.representation, to.type, to.constness);
|
||||
|
||||
expectations2.SetDataField(kDiffProp, expected.constness,
|
||||
expected.representation, expected.type);
|
||||
|
||||
// |map2| should be left unchanged but marked unstable.
|
||||
CHECK(!map2->is_deprecated());
|
||||
CHECK_EQ(*map2, *new_map);
|
||||
CHECK(expectations2.Check(*map2));
|
||||
|
||||
// In trivial case |map2| should be returned as a result of property
|
||||
// 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.constness,
|
||||
expected.representation, expected.type);
|
||||
CHECK(!map->is_deprecated());
|
||||
CHECK_NE(*map, *new_map);
|
||||
CHECK_EQ(expected_field_type_dependency, dependencies.HasAborted());
|
||||
dependencies.Rollback(); // Properly cleanup compilation info.
|
||||
|
||||
CHECK(!new_map->is_deprecated());
|
||||
CHECK(expectations.Check(*map));
|
||||
|
||||
// Field type updates must be properly propagated till fields' owner maps.
|
||||
Handle<Map> tmp_map = map;
|
||||
for (int nof = map->NumberOfOwnDescriptors(); nof >= 0; nof--) {
|
||||
CHECK(expectations.Check(*tmp_map, nof));
|
||||
tmp_map = handle(Map::cast(tmp_map->GetBackPointer()), isolate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ReconfigureElementsKind_GeneralizeSmiFieldToDouble) {
|
||||
CcTest::InitializeVM();
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
@ -2069,6 +2171,206 @@ TEST(ReconfigureElementsKind_GeneralizeHeapObjectFieldToTagged) {
|
||||
{kMutable, Representation::Tagged(), any_type});
|
||||
}
|
||||
|
||||
TEST(GeneralizeHeapObjectFieldToHeapObject_ThroughTransitionShortcuts) {
|
||||
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;
|
||||
|
||||
if (FLAG_track_constant_fields) {
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kConst, Representation::HeapObject(), new_type},
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kConst, Representation::HeapObject(), expected_type});
|
||||
|
||||
if (FLAG_modify_map_inplace) {
|
||||
// kConst to kMutable migration does not create a new map, therefore
|
||||
// trivial generalization.
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), new_type},
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type});
|
||||
}
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kConst, Representation::HeapObject(), new_type},
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type});
|
||||
}
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), new_type},
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type});
|
||||
|
||||
// Check that generalizations do not trigger deopts through elements kind
|
||||
// transition shortcuts when it's not necessary (for example, when the
|
||||
// shortcut map already has field types that don't have to be updated).
|
||||
|
||||
if (FLAG_track_constant_fields) {
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kConst, Representation::HeapObject(), new_type},
|
||||
{kConst, Representation::HeapObject(), any_type},
|
||||
{kConst, Representation::HeapObject(), expected_type}, false);
|
||||
|
||||
if (FLAG_modify_map_inplace) {
|
||||
// kConst to kMutable migration does not create a new map, therefore
|
||||
// trivial generalization.
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kConst, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), new_type},
|
||||
{kMutable, Representation::HeapObject(), any_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type}, false);
|
||||
}
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kConst, Representation::HeapObject(), new_type},
|
||||
{kMutable, Representation::HeapObject(), any_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type}, false);
|
||||
}
|
||||
TestGeneralizeFieldTrivial_ThroughTransitionShortcuts(
|
||||
{kMutable, Representation::HeapObject(), current_type},
|
||||
{kMutable, Representation::HeapObject(), new_type},
|
||||
{kMutable, Representation::HeapObject(), any_type},
|
||||
{kMutable, Representation::HeapObject(), expected_type}, false);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void CheckElementsKindTransitionShortcutsChain(
|
||||
Map* map, const std::vector<ElementsKind>& expected_elements_kinds) {
|
||||
Handle<Symbol> shortcut =
|
||||
map->GetIsolate()->factory()->elements_transition_shortcut_symbol();
|
||||
Map* current_map = map;
|
||||
CHECK_EQ(expected_elements_kinds[0], map->elements_kind());
|
||||
for (size_t i = 1; i < expected_elements_kinds.size(); i++) {
|
||||
Map* transition = TransitionArray::SearchSpecial(current_map, *shortcut);
|
||||
CHECK_NOT_NULL(transition);
|
||||
CHECK_EQ(expected_elements_kinds[i], transition->elements_kind());
|
||||
current_map = transition;
|
||||
}
|
||||
Map* transition = TransitionArray::SearchSpecial(current_map, *shortcut);
|
||||
CHECK_NULL(transition);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(MapInsertElementsKindTransitionShortcut) {
|
||||
CcTest::InitializeVM();
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
Handle<FieldType> any_type = FieldType::Any(isolate);
|
||||
|
||||
// Create a map with initial elements kind and add a property.
|
||||
Handle<Map> initial_map = Map::Create(isolate, 0);
|
||||
initial_map->set_elements_kind(PACKED_SMI_ELEMENTS);
|
||||
|
||||
Handle<String> field_name = MakeString("tmp");
|
||||
Handle<Map> map =
|
||||
Map::CopyWithField(initial_map, field_name, any_type, NONE, kMutable,
|
||||
Representation::Smi(), INSERT_TRANSITION)
|
||||
.ToHandleChecked();
|
||||
|
||||
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
|
||||
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
|
||||
STATIC_ASSERT(PACKED_ELEMENTS == 2);
|
||||
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
|
||||
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
|
||||
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
|
||||
const int kTransitionsCount = 6;
|
||||
MapHandles maps;
|
||||
for (int i = 0; i < kTransitionsCount; i++) {
|
||||
ElementsKind elements_kind = static_cast<ElementsKind>(i);
|
||||
Handle<Map> transition = Map::ReconfigureElementsKind(map, elements_kind);
|
||||
maps.push_back(transition);
|
||||
}
|
||||
|
||||
// Now create two chains of shortcuts.
|
||||
// 1) PACKED_SMI_ELEMENTS -> HOLEY_DOUBLE_ELEMENTS -> PACKED_ELEMENTS
|
||||
{
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_SMI_ELEMENTS], maps[PACKED_ELEMENTS]);
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_SMI_ELEMENTS], maps[HOLEY_DOUBLE_ELEMENTS]);
|
||||
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[HOLEY_DOUBLE_ELEMENTS], maps[PACKED_ELEMENTS]);
|
||||
}
|
||||
|
||||
// 2) HOLEY_SMI_ELEMENTS -> PACKED_DOUBLE_ELEMENTS -> HOLEY_ELEMENTS
|
||||
{
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[HOLEY_SMI_ELEMENTS], maps[HOLEY_ELEMENTS]);
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[HOLEY_SMI_ELEMENTS], maps[PACKED_DOUBLE_ELEMENTS]);
|
||||
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_DOUBLE_ELEMENTS], maps[HOLEY_ELEMENTS]);
|
||||
}
|
||||
|
||||
// Then create shortcut PACKED_SMI_ELEMENTS -> HOLEY_SMI_ELEMENTS.
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_SMI_ELEMENTS], maps[HOLEY_SMI_ELEMENTS]);
|
||||
|
||||
DCHECK(TransitionArray::IsSortedNoDuplicates(*initial_map));
|
||||
DCHECK(TransitionArray::IsConsistentWithBackPointers(*initial_map));
|
||||
|
||||
// As a result we should get full chain of transition shortcuts.
|
||||
CheckElementsKindTransitionShortcutsChain(
|
||||
*map, {PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
|
||||
HOLEY_DOUBLE_ELEMENTS, PACKED_ELEMENTS, HOLEY_ELEMENTS});
|
||||
|
||||
//
|
||||
// Now check that deprecated maps are handled properly.
|
||||
//
|
||||
|
||||
// Mark PACKED_DOUBLE_ELEMENTS and HOLEY_DOUBLE_ELEMENTS maps as deprecated.
|
||||
Map::ReconfigureProperty(maps[PACKED_DOUBLE_ELEMENTS], 0, kData, NONE,
|
||||
Representation::Tagged(), any_type, kMutable);
|
||||
CHECK(maps[PACKED_DOUBLE_ELEMENTS]->is_deprecated());
|
||||
|
||||
Map::ReconfigureProperty(maps[HOLEY_DOUBLE_ELEMENTS], 0, kData, NONE,
|
||||
Representation::Tagged(), any_type, kMutable);
|
||||
CHECK(maps[HOLEY_DOUBLE_ELEMENTS]->is_deprecated());
|
||||
|
||||
// And insert PACKED_SMI_ELEMENTS -> HOLEY_ELEMENTS shortcut again.
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_SMI_ELEMENTS], maps[HOLEY_ELEMENTS]);
|
||||
|
||||
// Deprecated maps should be removed from the chain.
|
||||
CheckElementsKindTransitionShortcutsChain(
|
||||
*map, {PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_ELEMENTS,
|
||||
HOLEY_ELEMENTS});
|
||||
|
||||
// Now update deprecated maps and insert shortcuts back into the chain.
|
||||
{
|
||||
Handle<Map> updated_map = Map::Update(maps[PACKED_DOUBLE_ELEMENTS]);
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[PACKED_SMI_ELEMENTS], updated_map);
|
||||
}
|
||||
{
|
||||
Handle<Map> updated_map = Map::Update(maps[HOLEY_DOUBLE_ELEMENTS]);
|
||||
Map::InsertElementsKindTransitionShortcutForTesting(
|
||||
isolate, maps[HOLEY_SMI_ELEMENTS], updated_map);
|
||||
}
|
||||
|
||||
// As a result we should get full chain of transition shortcuts again.
|
||||
CheckElementsKindTransitionShortcutsChain(
|
||||
*map, {PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
|
||||
HOLEY_DOUBLE_ELEMENTS, PACKED_ELEMENTS, HOLEY_ELEMENTS});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// A set of tests checking split map deprecation.
|
||||
//
|
||||
|
25
test/mjsunit/regress/regress-crbug-738763.js
Normal file
25
test/mjsunit/regress/regress-crbug-738763.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// Flags: --verify-heap --allow-natives-syntax --expose-gc
|
||||
|
||||
let constant = { a: 1 };
|
||||
|
||||
function update_array(array) {
|
||||
array.x = constant;
|
||||
%HeapObjectVerify(array);
|
||||
array[0] = undefined;
|
||||
%HeapObjectVerify(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
let ar1 = [1];
|
||||
let ar2 = [2];
|
||||
let ar3 = [3];
|
||||
gc();
|
||||
gc();
|
||||
|
||||
update_array(ar1);
|
||||
constant = update_array(ar2);
|
||||
update_array(ar3);
|
13
test/mjsunit/regress/regress-crbug-742346.js
Normal file
13
test/mjsunit/regress/regress-crbug-742346.js
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// Flags: --expose-gc --throws
|
||||
|
||||
TypeError.prototype.__defineGetter__("name", function() {
|
||||
this[1] = {};
|
||||
gc();
|
||||
new Uint16Array().reduceRight();
|
||||
});
|
||||
var v = WebAssembly.compile();
|
||||
new TypeError().toString();
|
12
test/mjsunit/regress/regress-crbug-742381.js
Normal file
12
test/mjsunit/regress/regress-crbug-742381.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2017 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 f(v) {
|
||||
v.x = 0;
|
||||
v[1] = 0.1;
|
||||
v.x = {};
|
||||
}
|
||||
f({});
|
||||
f(new Array(1));
|
||||
f(new Array(1));
|
Loading…
Reference in New Issue
Block a user