Commit c285380c authored by Shiyu Zhang's avatar Shiyu Zhang Committed by Commit Bot

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: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56303}
parent 0b9f6476
......@@ -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_;
}
......
......@@ -2386,6 +2386,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());
......
......@@ -4882,6 +4882,25 @@ 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());
SLOW_DCHECK(target == nullptr ||
Map::TryUpdateSlow(isolate, old_map) == nullptr ||
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;
......@@ -4889,6 +4908,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()) {
......@@ -4897,23 +4932,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) {
......@@ -4995,6 +5028,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();
}
......
......@@ -632,6 +632,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,18 @@ int TransitionsAccessor::NumberOfTransitions() {
return 0; // Make GCC happy.
}
void TransitionsAccessor::SetMigrationTarget(Map* migration_target) {
DCHECK(map_->is_deprecated());
ReplaceTransitions(MaybeObject::FromObject(migration_target));
}
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 +493,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 +517,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