Commit 6ec90ece authored by Shiyu Zhang's avatar Shiyu Zhang Committed by Commit Bot

Reland "Create a fast path to get migration target when updating map"

This is a reland of c285380c

Original change's description:
> Create a fast path to get migration target when updating map
>
> During map updating, store the pointer to new map in the
> raw_transitions slot of the old map that is deprecated from map
> transition tree. Thus, we can get the migration target directly
> instead of TryReplayPropertyTransitions when updating map.
>
> This can improve Speedometer2.0 Elm-TodoMVC case by ~5% on ATOM
> Chromebook and ~9% on big-core Ubuntu.
>
> Change-Id: I56f9ce5183bbdd567b964890f623ef0ceed9b7db
> Reviewed-on: https://chromium-review.googlesource.com/1233433
> Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#56303}

Change-Id: Idf0b7716b92a6a15bfe58721c2c34dbd02b31137
Reviewed-on: https://chromium-review.googlesource.com/c/1270261Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com>
Cr-Commit-Position: refs/heads/master@{#56588}
parent 56b8ab5d
......@@ -170,6 +170,7 @@ Handle<Map> MapUpdater::Update() {
if (FindTargetMap() == kEnd) return result_map_;
ConstructNewMap();
DCHECK_EQ(kEnd, state_);
TransitionsAccessor(isolate_, old_map_).SetMigrationTarget(*result_map_);
return result_map_;
}
......
......@@ -2434,6 +2434,7 @@ void TransitionsAccessor::PrintTransitions(std::ostream& os) { // NOLINT
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
return;
case kWeakRef: {
Map* target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
......
......@@ -4904,6 +4904,45 @@ Handle<Map> Map::ReconfigureElementsKind(Isolate* isolate, Handle<Map> map,
return mu.ReconfigureElementsKind(new_elements_kind);
}
namespace {
Map* SearchMigrationTarget(Isolate* isolate, Map* old_map) {
DisallowHeapAllocation no_allocation;
DisallowDeoptimization no_deoptimization(isolate);
Map* target = old_map;
do {
target = TransitionsAccessor(isolate, target, &no_allocation)
.GetMigrationTarget();
} while (target != nullptr && target->is_deprecated());
if (target == nullptr) return nullptr;
// TODO(ishell): if this validation ever become a bottleneck consider adding a
// bit to the Map telling whether it contains fields whose field types may be
// cleared.
// TODO(ishell): revisit handling of cleared field types in
// TryReplayPropertyTransitions() and consider checking the target map's field
// types instead of old_map's types.
// Go to slow map updating if the old_map has fast properties with cleared
// field types.
int old_nof = old_map->NumberOfOwnDescriptors();
DescriptorArray* old_descriptors = old_map->instance_descriptors();
for (int i = 0; i < old_nof; i++) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.location() == kField && old_details.kind() == kData) {
FieldType* old_type = old_descriptors->GetFieldType(i);
if (FieldTypeIsCleared(old_details.representation(), old_type)) {
return nullptr;
}
}
}
SLOW_DCHECK(Map::TryUpdateSlow(isolate, old_map) == target);
return target;
}
} // namespace
// TODO(ishell): Move TryUpdate() and friends to MapUpdater
// static
MaybeHandle<Map> Map::TryUpdate(Isolate* isolate, Handle<Map> old_map) {
DisallowHeapAllocation no_allocation;
......@@ -4911,6 +4950,22 @@ MaybeHandle<Map> Map::TryUpdate(Isolate* isolate, Handle<Map> old_map) {
if (!old_map->is_deprecated()) return old_map;
Map* target_map = SearchMigrationTarget(isolate, *old_map);
if (target_map != nullptr) {
return handle(target_map, isolate);
}
Map* new_map = TryUpdateSlow(isolate, *old_map);
if (new_map == nullptr) return MaybeHandle<Map>();
TransitionsAccessor(isolate, *old_map, &no_allocation)
.SetMigrationTarget(new_map);
return handle(new_map, isolate);
}
Map* Map::TryUpdateSlow(Isolate* isolate, Map* old_map) {
DisallowHeapAllocation no_allocation;
DisallowDeoptimization no_deoptimization(isolate);
// Check the state of the root map.
Map* root_map = old_map->FindRootMap(isolate);
if (root_map->is_deprecated()) {
......@@ -4919,23 +4974,21 @@ MaybeHandle<Map> Map::TryUpdate(Isolate* isolate, Handle<Map> old_map) {
DCHECK(constructor->initial_map()->is_dictionary_map());
if (constructor->initial_map()->elements_kind() !=
old_map->elements_kind()) {
return MaybeHandle<Map>();
return nullptr;
}
return handle(constructor->initial_map(), constructor->GetIsolate());
return constructor->initial_map();
}
if (!old_map->EquivalentToForTransition(root_map)) return MaybeHandle<Map>();
if (!old_map->EquivalentToForTransition(root_map)) return nullptr;
ElementsKind from_kind = root_map->elements_kind();
ElementsKind to_kind = old_map->elements_kind();
if (from_kind != to_kind) {
// Try to follow existing elements kind transitions.
root_map = root_map->LookupElementsTransitionMap(isolate, to_kind);
if (root_map == nullptr) return MaybeHandle<Map>();
if (root_map == nullptr) return nullptr;
// From here on, use the map with correct elements kind as root map.
}
Map* new_map = root_map->TryReplayPropertyTransitions(isolate, *old_map);
if (new_map == nullptr) return MaybeHandle<Map>();
return handle(new_map, isolate);
return root_map->TryReplayPropertyTransitions(isolate, old_map);
}
Map* Map::TryReplayPropertyTransitions(Isolate* isolate, Map* old_map) {
......@@ -5017,6 +5070,10 @@ Map* Map::TryReplayPropertyTransitions(Isolate* isolate, Map* old_map) {
// static
Handle<Map> Map::Update(Isolate* isolate, Handle<Map> map) {
if (!map->is_deprecated()) return map;
Map* target_map = SearchMigrationTarget(isolate, *map);
if (target_map != nullptr) {
return handle(target_map, isolate);
}
MapUpdater mu(isolate, map);
return mu.Update();
}
......
......@@ -633,6 +633,7 @@ class Map : public HeapObject {
// is found.
static MaybeHandle<Map> TryUpdate(Isolate* isolate,
Handle<Map> map) V8_WARN_UNUSED_RESULT;
static Map* TryUpdateSlow(Isolate* isolate, Map* map) V8_WARN_UNUSED_RESULT;
// Returns a non-deprecated version of the input. This method may deprecate
// existing maps along the way if encodings conflict. Not for use while
......
......@@ -65,6 +65,7 @@ Name* TransitionsAccessor::GetKey(int transition_number) {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
UNREACHABLE();
return nullptr;
case kWeakRef: {
......@@ -118,6 +119,7 @@ Map* TransitionsAccessor::GetTarget(int transition_number) {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
UNREACHABLE();
return nullptr;
case kWeakRef:
......
......@@ -21,9 +21,12 @@ void TransitionsAccessor::Initialize() {
} else if (raw_transitions_->GetHeapObjectIfStrong(&heap_object)) {
if (heap_object->IsTransitionArray()) {
encoding_ = kFullTransitionArray;
} else {
DCHECK(heap_object->IsPrototypeInfo());
} else if (heap_object->IsPrototypeInfo()) {
encoding_ = kPrototypeInfo;
} else {
DCHECK(map_->is_deprecated());
DCHECK(heap_object->IsMap());
encoding_ = kMigrationTarget;
}
} else {
UNREACHABLE();
......@@ -48,6 +51,7 @@ bool TransitionsAccessor::HasSimpleTransitionTo(Map* map) {
return raw_transitions_->GetHeapObjectAssumeWeak() == map;
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
case kFullTransitionArray:
return false;
}
......@@ -212,6 +216,7 @@ Map* TransitionsAccessor::SearchTransition(Name* name, PropertyKind kind,
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
return nullptr;
case kWeakRef: {
Map* map = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
......@@ -264,6 +269,7 @@ Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
case kFullTransitionArray:
return Handle<String>::null();
case kWeakRef: {
......@@ -430,6 +436,7 @@ int TransitionsAccessor::NumberOfTransitions() {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
return 0;
case kWeakRef:
return 1;
......@@ -440,6 +447,26 @@ int TransitionsAccessor::NumberOfTransitions() {
return 0; // Make GCC happy.
}
void TransitionsAccessor::SetMigrationTarget(Map* migration_target) {
DCHECK(map_->is_deprecated());
if (encoding() == kFullTransitionArray) {
// Transition arrays are not shared. When one is replaced, it should not
// keep referenced objects alive, so we zap it.
// When there is another reference to the array somewhere (e.g. a handle),
// not zapping turns from a waste of memory into a source of crashes.
transitions()->Zap(isolate_);
}
map_->set_raw_transitions(MaybeObject::FromObject(migration_target));
MarkNeedsReload();
}
Map* TransitionsAccessor::GetMigrationTarget() {
if (encoding() == kMigrationTarget) {
return map_->raw_transitions()->cast<Map>();
}
return nullptr;
}
void TransitionArray::Zap(Isolate* isolate) {
MemsetPointer(
data_start() + kPrototypeTransitionsIndex,
......@@ -474,7 +501,8 @@ void TransitionsAccessor::SetPrototypeTransitions(
void TransitionsAccessor::EnsureHasFullTransitionArray() {
if (encoding() == kFullTransitionArray) return;
int nof = encoding() == kUninitialized ? 0 : 1;
int nof =
(encoding() == kUninitialized || encoding() == kMigrationTarget) ? 0 : 1;
Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(nof);
Reload(); // Reload after possible GC.
if (nof == 1) {
......@@ -497,6 +525,7 @@ void TransitionsAccessor::TraverseTransitionTreeInternal(
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
break;
case kWeakRef: {
Map* simple_target =
......
......@@ -106,6 +106,14 @@ class TransitionsAccessor {
void PutPrototypeTransition(Handle<Object> prototype, Handle<Map> target_map);
Handle<Map> GetPrototypeTransition(Handle<Object> prototype);
// During the first-time Map::Update and Map::TryUpdate, the migration target
// map could be cached in the raw_transitions slot of the old map that is
// deprecated from the map transition tree. The next time old map is updated,
// we will check this cache slot as a shortcut to get the migration target
// map.
void SetMigrationTarget(Map* migration_target);
Map* GetMigrationTarget();
#if DEBUG || OBJECT_PRINT
void PrintTransitions(std::ostream& os);
static void PrintOneTransition(std::ostream& os, Name* key, Map* target);
......@@ -125,6 +133,7 @@ class TransitionsAccessor {
enum Encoding {
kPrototypeInfo,
kUninitialized,
kMigrationTarget,
kWeakRef,
kFullTransitionArray,
};
......
......@@ -78,6 +78,14 @@ static Handle<AccessorPair> CreateAccessorPair(bool with_getter,
return pair;
}
// Check cached migration target map after Map::Update() and Map::TryUpdate()
static void CheckMigrationTarget(Isolate* isolate, Map* old_map, Map* new_map) {
Map* target = TransitionsAccessor(isolate, handle(old_map, isolate))
.GetMigrationTarget();
if (!target) return;
CHECK_EQ(new_map, target);
CHECK_EQ(Map::TryUpdateSlow(isolate, old_map), target);
}
class Expectations {
static const int MAX_PROPERTIES = 10;
......@@ -707,6 +715,7 @@ static void TestGeneralizeField(int detach_property_at_index,
// Update all deprecated maps and check that they are now the same.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*new_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
}
static void TestGeneralizeField(const CRFTData& from, const CRFTData& to,
......@@ -967,9 +976,11 @@ TEST(GeneralizeFieldWithAccessorProperties) {
// Update all deprecated maps and check that they are now the same.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*active_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
for (int i = 0; i < kPropCount; i++) {
updated_map = Map::Update(isolate, maps[i]);
CHECK_EQ(*active_map, *updated_map);
CheckMigrationTarget(isolate, *maps[i], *updated_map);
}
}
......@@ -1060,6 +1071,7 @@ static void TestReconfigureDataFieldAttribute_GeneralizeField(
// Update deprecated |map|, it should become |new_map|.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*new_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
}
// This test ensures that trivial field generalization (from HeapObject to
......@@ -1370,6 +1382,7 @@ struct CheckDeprecated {
// Update deprecated |map|, it should become |new_map|.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*new_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
}
};
......@@ -1828,6 +1841,7 @@ static void TestReconfigureElementsKind_GeneralizeField(
// Update deprecated |map|, it should become |new_map|.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*new_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
// Ensure Map::FindElementsKindTransitionedMap() is able to find the
// transitioned map.
......@@ -2339,9 +2353,11 @@ static void TestGeneralizeFieldWithSpecialTransition(TestConfig& config,
// Update all deprecated maps and check that they are now the same.
Handle<Map> updated_map = Map::Update(isolate, map);
CHECK_EQ(*active_map, *updated_map);
CheckMigrationTarget(isolate, *map, *updated_map);
for (int i = 0; i < kPropCount; i++) {
updated_map = Map::Update(isolate, maps[i]);
CHECK_EQ(*active_map, *updated_map);
CheckMigrationTarget(isolate, *maps[i], *updated_map);
}
}
......@@ -2623,6 +2639,7 @@ struct FieldGeneralizationChecker {
CHECK_NE(*map1, *map2);
Handle<Map> updated_map = Map::Update(isolate, map1);
CHECK_EQ(*map2, *updated_map);
CheckMigrationTarget(isolate, *map1, *updated_map);
expectations2.SetDataField(descriptor_, attributes_, constness_,
representation_, heap_type_);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment