[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}
This commit is contained in:
Igor Sheludko 2017-07-13 10:41:47 +02:00 committed by Commit Bot
parent c7be8081fc
commit b90e83f5da
14 changed files with 519 additions and 75 deletions

View File

@ -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) \

View File

@ -1437,7 +1437,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));
@ -2128,10 +2128,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);
}
}

View File

@ -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

View File

@ -1624,7 +1624,13 @@ static bool CheckOneBackPointer(Map* current_map, Object* target) {
// static
bool TransitionArray::IsConsistentWithBackPointers(Map* map) {
Object* transitions = map->raw_transitions();
Symbol* elements_transition_shortcut_symbol =
map->GetHeap()->elements_transition_shortcut_symbol();
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 (name == elements_transition_shortcut_symbol) continue;
Map* target = TransitionArray::GetTarget(transitions, i);
if (!CheckOneBackPointer(map, target)) return false;
}

View File

@ -3193,6 +3193,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;

View File

@ -1718,6 +1718,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 {
@ -1764,17 +1767,19 @@ 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());
} 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,

View File

@ -4234,13 +4234,21 @@ 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;
Symbol* elements_transition_shortcut_symbol =
isolate->heap()->elements_transition_shortcut_symbol();
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 (key == elements_transition_shortcut_symbol) continue;
target->DeprecateTransitionTree(isolate);
}
DCHECK(!constructor_or_backpointer()->IsFunctionTemplateInfo());
deprecate();
@ -4308,16 +4316,19 @@ 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_elements_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_elements_transition_shortcut;
DCHECK_EQ(kData, details.kind());
Symbol* elements_transition_shortcut_symbol =
GetHeap()->elements_transition_shortcut_symbol();
Zone zone(GetIsolate()->allocator(), ZONE_NAME);
ZoneQueue<Map*> backlog(&zone);
@ -4330,7 +4341,16 @@ 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 (key == elements_transition_shortcut_symbol) {
// Handling of elements kind transition shortcuts may also require
// deoptimization of dependent code, Map::GeneralizeField() takes
// care of this case.
has_elements_transition_shortcut = true;
continue;
}
backlog.push(target);
}
DescriptorArray* descriptors = current->instance_descriptors();
@ -4354,6 +4374,7 @@ void Map::UpdateFieldType(int descriptor, Handle<Name> name,
descriptors->Replace(descriptor, &d);
}
}
return has_elements_transition_shortcut;
}
bool FieldTypeIsCleared(Representation rep, FieldType* type) {
@ -4378,7 +4399,6 @@ Handle<FieldType> Map::GeneralizeFieldType(Representation rep1,
return FieldType::Any(isolate);
}
// static
void Map::GeneralizeField(Handle<Map> map, int modify_index,
PropertyConstness new_constness,
@ -4386,58 +4406,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);
}
}
@ -4447,14 +4482,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) {
@ -9159,6 +9194,114 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
return new_map;
}
// 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);
Handle<Symbol> name =
isolate->factory()->elements_transition_shortcut_symbol();
ElementsKind transition_elements_kind = transition->elements_kind();
for (;;) {
Map* maybe_transition = TransitionArray::SearchSpecial(*map, *name);
// If shortcut to the |transition| is already added then we are done.
if (maybe_transition == *transition) return;
// If we reach the end of elements kind transition chain then this is
// point where we are going to add the shortcut |map|->|transition|.
if (maybe_transition == NULL) break;
Handle<Map> next_map(maybe_transition, isolate);
DCHECK_NE(maybe_transition->elements_kind(), transition_elements_kind);
if (!IsMoreGeneralElementsKindTransition(maybe_transition->elements_kind(),
transition_elements_kind)) {
// |maybe_transition| has more general elements kind than |transition|,
// so rewire the chain this way: |map|->|transition|->|maybe_transition|.
Map::ConnectTransition(map, transition, name,
SPECIAL_SHORTCUT_TRANSITION);
Map::ConnectTransition(transition, next_map, name,
SPECIAL_SHORTCUT_TRANSITION);
return;
}
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());
@ -9420,7 +9563,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);

View File

@ -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 {

View File

@ -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,10 @@ class Map : public HeapObject {
// not found.
Map* TryReplayPropertyTransitions(Map* map);
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 +812,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 +820,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);

View File

@ -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());
@ -113,6 +125,7 @@ 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();
}
#endif

View File

@ -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());

View File

@ -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.

View File

@ -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,134 @@ 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);
}
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> map = Map::Create(isolate, 0);
map->set_elements_kind(PACKED_SMI_ELEMENTS);
{
Handle<String> name = MakeString("tmp");
map = Map::CopyWithField(map, name, any_type, NONE, kMutable,
Representation::Tagged(), INSERT_TRANSITION)
.ToHandleChecked();
}
static const ElementsKind elements_kinds[] = {
HOLEY_SMI_ELEMENTS, PACKED_ELEMENTS, HOLEY_ELEMENTS,
PACKED_DOUBLE_ELEMENTS, HOLEY_DOUBLE_ELEMENTS,
};
const int kTransitionsCount = arraysize(elements_kinds);
MapHandles transitions;
for (int i = 0; i < kTransitionsCount; i++) {
Handle<Map> transition =
Map::ReconfigureElementsKind(map, elements_kinds[i]);
transitions.push_back(transition);
}
// Now insert elements kind transition shortcuts from |map| in random order
// and ensure that the transitions are properly ordered.
v8::base::RandomNumberGenerator rand_gen(FLAG_random_seed);
for (int i = 0; i < kTransitionsCount * 3; i++) {
size_t index = rand_gen.NextInt() % transitions.size();
Handle<Map> transition(transitions[index]);
Map::InsertElementsKindTransitionShortcutForTesting(isolate, map,
transition);
}
Handle<Symbol> name =
isolate->factory()->elements_transition_shortcut_symbol();
Map* current_map = *map;
for (;;) {
Map* transition = TransitionArray::SearchSpecial(current_map, *name);
if (transition == NULL) break;
CHECK_NE(current_map->elements_kind(), transition->elements_kind());
CHECK(IsMoreGeneralElementsKindTransition(current_map->elements_kind(),
transition->elements_kind()));
current_map = transition;
}
}
////////////////////////////////////////////////////////////////////////////////
// A set of tests checking split map deprecation.
//

View 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);