v8/src/transitions.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

665 lines
24 KiB
C++
Raw Normal View History

// Copyright 2012 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.
#include "src/transitions.h"
#include "src/objects-inl.h"
#include "src/transitions-inl.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
void TransitionsAccessor::Initialize() {
raw_transitions_ = map_->raw_transitions();
HeapObject* heap_object;
if (raw_transitions_->IsSmi() || raw_transitions_->IsCleared()) {
encoding_ = kUninitialized;
} else if (raw_transitions_->IsWeak()) {
encoding_ = kWeakRef;
} else if (raw_transitions_->GetHeapObjectIfStrong(&heap_object)) {
if (heap_object->IsTransitionArray()) {
encoding_ = kFullTransitionArray;
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
} else if (heap_object->IsPrototypeInfo()) {
Revert "Reland "Create a fast path to get migration target when updating map"" This reverts commit 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8. Reason for revert: causes a lot of Canary crashes (chromium:895208). GC relies on an the fact that the transition array stays alive while it's owner map is alive (this is needed in order to properly transfer descriptor array ownership to the parent map when the map owning a shared descriptor array dies). We need to rethink a way of caching the migration target shortcut. Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} TBR=ishell@chromium.org,shiyu.zhang@intel.com # Not skipping CQ checks because original CL landed > 1 day ago. Change-Id: Ie7e9b98395b041a1095da549d1cd71d7180a4888 Bug: chromium:895208 Reviewed-on: https://chromium-review.googlesource.com/c/1280223 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#56641}
2018-10-15 13:36:53 +00:00
encoding_ = kPrototypeInfo;
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
} else {
DCHECK(map_->is_deprecated());
DCHECK(heap_object->IsMap());
encoding_ = kMigrationTarget;
}
} else {
UNREACHABLE();
}
#if DEBUG
needs_reload_ = false;
#endif
}
Map TransitionsAccessor::GetSimpleTransition() {
switch (encoding()) {
case kWeakRef:
return Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
default:
return Map();
}
}
bool TransitionsAccessor::HasSimpleTransitionTo(Map map) {
switch (encoding()) {
case kWeakRef:
return raw_transitions_->GetHeapObjectAssumeWeak() == map;
case kPrototypeInfo:
case kUninitialized:
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
case kMigrationTarget:
case kFullTransitionArray:
return false;
}
UNREACHABLE();
}
void TransitionsAccessor::Insert(Handle<Name> name, Handle<Map> target,
SimpleTransitionFlag flag) {
DCHECK(!map_handle_.is_null());
target->SetBackPointer(map_);
// If the map doesn't have any transitions at all yet, install the new one.
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
if (encoding() == kUninitialized || encoding() == kMigrationTarget) {
if (flag == SIMPLE_PROPERTY_TRANSITION) {
ReplaceTransitions(HeapObjectReference::Weak(*target));
return;
}
// If the flag requires a full TransitionArray, allocate one.
Handle<TransitionArray> result =
isolate_->factory()->NewTransitionArray(0, 1);
ReplaceTransitions(MaybeObject::FromObject(*result));
Reload();
}
Revert "Reland "[runtime] Add shortcuts for elements kinds transitions."" This reverts commit 6e27386d68eb737b98d37cb32e14d6bea1fd62cd. Reason for revert: There will be another much simpler and back-mergeable fix. Original change's description: > Reland "[runtime] Add shortcuts for elements kinds transitions." > > This is a reland of b90e83f5da40b214646327f8791834eeb5ddedd4 > 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} TBR=ulan@chromium.org,jkummerow@chromium.org,ishell@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: chromium:738763, chromium:742346, chromium:742381, chromium:745844 Change-Id: I203dc748c47db554e0a86d61f0e2b7b8b96f2370 Reviewed-on: https://chromium-review.googlesource.com/581547 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#46826}
2017-07-21 15:57:33 +00:00
bool is_special_transition = flag == SPECIAL_TRANSITION;
// If the map has a simple transition, check if it should be overwritten.
Map simple_transition = GetSimpleTransition();
if (!simple_transition.is_null()) {
Name key = GetSimpleTransitionKey(simple_transition);
PropertyDetails old_details = GetSimpleTargetDetails(simple_transition);
PropertyDetails new_details = is_special_transition
? PropertyDetails::Empty()
: GetTargetDetails(*name, *target);
if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) &&
old_details.kind() == new_details.kind() &&
old_details.attributes() == new_details.attributes()) {
ReplaceTransitions(HeapObjectReference::Weak(*target));
return;
}
// Otherwise allocate a full TransitionArray with slack for a new entry.
Handle<Map> map(simple_transition, isolate_);
Handle<TransitionArray> result =
isolate_->factory()->NewTransitionArray(1, 1);
// Reload state; allocations might have caused it to be cleared.
Reload();
simple_transition = GetSimpleTransition();
if (!simple_transition.is_null()) {
DCHECK_EQ(*map, simple_transition);
if (encoding_ == kWeakRef) {
result->Set(0, GetSimpleTransitionKey(simple_transition),
HeapObjectReference::Weak(simple_transition));
} else {
UNREACHABLE();
}
} else {
result->SetNumberOfTransitions(0);
}
ReplaceTransitions(MaybeObject::FromObject(*result));
Reload();
}
// At this point, we know that the map has a full TransitionArray.
DCHECK_EQ(kFullTransitionArray, encoding());
int number_of_transitions = 0;
int new_nof = 0;
int insertion_index = kNotFound;
DCHECK_EQ(is_special_transition,
IsSpecialTransition(ReadOnlyRoots(isolate_), *name));
PropertyDetails details = is_special_transition
? PropertyDetails::Empty()
: GetTargetDetails(*name, *target);
{
DisallowHeapAllocation no_gc;
TransitionArray array = transitions();
number_of_transitions = array->number_of_transitions();
new_nof = number_of_transitions;
int index =
is_special_transition
? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
: array->Search(details.kind(), *name, details.attributes(),
&insertion_index);
// If an existing entry was found, overwrite it and return.
if (index != kNotFound) {
array->SetRawTarget(index, HeapObjectReference::Weak(*target));
return;
}
++new_nof;
CHECK_LE(new_nof, kMaxNumberOfTransitions);
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
// If there is enough capacity, insert new entry into the existing array.
if (new_nof <= array->Capacity()) {
array->SetNumberOfTransitions(new_nof);
for (index = number_of_transitions; index > insertion_index; --index) {
array->SetKey(index, array->GetKey(index - 1));
array->SetRawTarget(index, array->GetRawTarget(index - 1));
}
array->SetKey(index, *name);
array->SetRawTarget(index, HeapObjectReference::Weak(*target));
SLOW_DCHECK(array->IsSortedNoDuplicates());
return;
}
}
// We're gonna need a bigger TransitionArray.
Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(
new_nof,
Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
// The map's transition array may have shrunk during the allocation above as
// it was weakly traversed, though it is guaranteed not to disappear. Trim the
// result copy if needed, and recompute variables.
Reload();
DisallowHeapAllocation no_gc;
TransitionArray array = transitions();
if (array->number_of_transitions() != number_of_transitions) {
DCHECK(array->number_of_transitions() < number_of_transitions);
number_of_transitions = array->number_of_transitions();
new_nof = number_of_transitions;
insertion_index = kNotFound;
int index =
is_special_transition
? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
: array->Search(details.kind(), *name, details.attributes(),
&insertion_index);
if (index == kNotFound) {
++new_nof;
} else {
insertion_index = index;
}
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
result->SetNumberOfTransitions(new_nof);
}
if (array->HasPrototypeTransitions()) {
result->SetPrototypeTransitions(array->GetPrototypeTransitions());
}
DCHECK_NE(kNotFound, insertion_index);
for (int i = 0; i < insertion_index; ++i) {
result->Set(i, array->GetKey(i), array->GetRawTarget(i));
}
result->Set(insertion_index, *name, HeapObjectReference::Weak(*target));
for (int i = insertion_index; i < number_of_transitions; ++i) {
result->Set(i + 1, array->GetKey(i), array->GetRawTarget(i));
}
SLOW_DCHECK(result->IsSortedNoDuplicates());
ReplaceTransitions(MaybeObject::FromObject(*result));
}
Map TransitionsAccessor::SearchTransition(Name name, PropertyKind kind,
PropertyAttributes attributes) {
DCHECK(name->IsUniqueName());
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
case kMigrationTarget:
return Map();
case kWeakRef: {
Map map = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
if (!IsMatchingMap(map, name, kind, attributes)) return Map();
return map;
}
case kFullTransitionArray: {
int transition = transitions()->Search(kind, name, attributes);
if (transition == kNotFound) return Map();
return transitions()->GetTarget(transition);
}
}
UNREACHABLE();
}
Map TransitionsAccessor::SearchSpecial(Symbol name) {
if (encoding() != kFullTransitionArray) return Map();
int transition = transitions()->SearchSpecial(name);
if (transition == kNotFound) return Map();
return transitions()->GetTarget(transition);
}
// static
bool TransitionsAccessor::IsSpecialTransition(ReadOnlyRoots roots, Name name) {
if (!name->IsSymbol()) return false;
return name == roots.nonextensible_symbol() ||
name == roots.sealed_symbol() || name == roots.frozen_symbol() ||
name == roots.elements_transition_symbol() ||
name == roots.strict_function_transition_symbol();
}
MaybeHandle<Map> TransitionsAccessor::FindTransitionToDataProperty(
Handle<Name> name, RequestedLocation requested_location) {
DCHECK(name->IsUniqueName());
DisallowHeapAllocation no_gc;
PropertyAttributes attributes = name->IsPrivate() ? DONT_ENUM : NONE;
Map target = SearchTransition(*name, kData, attributes);
if (target.is_null()) return MaybeHandle<Map>();
PropertyDetails details = target->GetLastDescriptorDetails();
DCHECK_EQ(attributes, details.attributes());
DCHECK_EQ(kData, details.kind());
if (requested_location == kFieldOnly && details.location() != kField) {
return MaybeHandle<Map>();
}
return Handle<Map>(target, isolate_);
}
Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
DisallowHeapAllocation no_gc;
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
case kMigrationTarget:
case kFullTransitionArray:
return Handle<String>::null();
case kWeakRef: {
Map target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
PropertyDetails details = GetSimpleTargetDetails(target);
if (details.location() != kField) return Handle<String>::null();
DCHECK_EQ(kData, details.kind());
if (details.attributes() != NONE) return Handle<String>::null();
Name name = GetSimpleTransitionKey(target);
if (!name->IsString()) return Handle<String>::null();
return handle(String::cast(name), isolate_);
}
}
UNREACHABLE();
}
Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() {
DCHECK(!ExpectedTransitionKey().is_null());
return handle(GetTarget(0), isolate_);
}
bool TransitionsAccessor::CanHaveMoreTransitions() {
if (map_->is_dictionary_map()) return false;
if (encoding() == kFullTransitionArray) {
return transitions()->number_of_transitions() < kMaxNumberOfTransitions;
}
return true;
}
// static
bool TransitionsAccessor::IsMatchingMap(Map target, Name name,
PropertyKind kind,
PropertyAttributes attributes) {
int descriptor = target->LastAdded();
DescriptorArray* descriptors = target->instance_descriptors();
Name key = descriptors->GetKey(descriptor);
if (key != name) return false;
PropertyDetails details = descriptors->GetDetails(descriptor);
return (details.kind() == kind && details.attributes() == attributes);
}
// static
bool TransitionArray::CompactPrototypeTransitionArray(Isolate* isolate,
WeakFixedArray array) {
const int header = kProtoTransitionHeaderSize;
int number_of_transitions = NumberOfPrototypeTransitions(array);
if (number_of_transitions == 0) {
// Empty array cannot be compacted.
return false;
}
int new_number_of_transitions = 0;
for (int i = 0; i < number_of_transitions; i++) {
MaybeObject target = array->Get(header + i);
DCHECK(target->IsCleared() ||
(target->IsWeak() && target->GetHeapObject()->IsMap()));
if (!target->IsCleared()) {
if (new_number_of_transitions != i) {
array->Set(header + new_number_of_transitions, target);
}
new_number_of_transitions++;
}
}
// Fill slots that became free with undefined value.
MaybeObject undefined =
MaybeObject::FromObject(*isolate->factory()->undefined_value());
for (int i = new_number_of_transitions; i < number_of_transitions; i++) {
array->Set(header + i, undefined);
}
if (number_of_transitions != new_number_of_transitions) {
SetNumberOfPrototypeTransitions(array, new_number_of_transitions);
}
return new_number_of_transitions < number_of_transitions;
}
// static
Handle<WeakFixedArray> TransitionArray::GrowPrototypeTransitionArray(
Handle<WeakFixedArray> array, int new_capacity, Isolate* isolate) {
// Grow array by factor 2 up to MaxCachedPrototypeTransitions.
int capacity = array->length() - kProtoTransitionHeaderSize;
new_capacity = Min(kMaxCachedPrototypeTransitions, new_capacity);
DCHECK_GT(new_capacity, capacity);
int grow_by = new_capacity - capacity;
array =
isolate->factory()->CopyWeakFixedArrayAndGrow(array, grow_by, TENURED);
if (capacity < 0) {
// There was no prototype transitions array before, so the size
// couldn't be copied. Initialize it explicitly.
SetNumberOfPrototypeTransitions(*array, 0);
}
return array;
}
void TransitionsAccessor::PutPrototypeTransition(Handle<Object> prototype,
Handle<Map> target_map) {
DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
// Don't cache prototype transition if this map is either shared, or a map of
// a prototype.
if (map_->is_prototype_map()) return;
if (map_->is_dictionary_map() || !FLAG_cache_prototype_transitions) return;
const int header = TransitionArray::kProtoTransitionHeaderSize;
Handle<WeakFixedArray> cache(GetPrototypeTransitions(), isolate_);
int capacity = cache->length() - header;
int transitions = TransitionArray::NumberOfPrototypeTransitions(*cache) + 1;
if (transitions > capacity) {
// Grow the array if compacting it doesn't free space.
if (!TransitionArray::CompactPrototypeTransitionArray(isolate_, *cache)) {
if (capacity == TransitionArray::kMaxCachedPrototypeTransitions) return;
cache = TransitionArray::GrowPrototypeTransitionArray(
cache, 2 * transitions, isolate_);
Reload();
SetPrototypeTransitions(cache);
}
}
// Reload number of transitions as they might have been compacted.
int last = TransitionArray::NumberOfPrototypeTransitions(*cache);
int entry = header + last;
cache->Set(entry, HeapObjectReference::Weak(*target_map));
TransitionArray::SetNumberOfPrototypeTransitions(*cache, last + 1);
}
Handle<Map> TransitionsAccessor::GetPrototypeTransition(
Handle<Object> prototype) {
DisallowHeapAllocation no_gc;
WeakFixedArray cache = GetPrototypeTransitions();
int length = TransitionArray::NumberOfPrototypeTransitions(cache);
for (int i = 0; i < length; i++) {
MaybeObject target =
cache->Get(TransitionArray::kProtoTransitionHeaderSize + i);
DCHECK(target->IsWeakOrCleared());
HeapObject* heap_object;
if (target->GetHeapObjectIfWeak(&heap_object)) {
Map map = Map::cast(heap_object);
if (map->prototype() == *prototype) {
return handle(map, isolate_);
}
}
}
return Handle<Map>();
}
WeakFixedArray TransitionsAccessor::GetPrototypeTransitions() {
if (encoding() != kFullTransitionArray ||
!transitions()->HasPrototypeTransitions()) {
return ReadOnlyRoots(isolate_).empty_weak_fixed_array();
}
return transitions()->GetPrototypeTransitions();
}
// static
void TransitionArray::SetNumberOfPrototypeTransitions(
WeakFixedArray proto_transitions, int value) {
DCHECK_NE(proto_transitions->length(), 0);
proto_transitions->Set(kProtoTransitionNumberOfEntriesOffset,
MaybeObject::FromSmi(Smi::FromInt(value)));
}
int TransitionsAccessor::NumberOfTransitions() {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
case kMigrationTarget:
return 0;
case kWeakRef:
return 1;
case kFullTransitionArray:
return transitions()->number_of_transitions();
}
UNREACHABLE();
return 0; // Make GCC happy.
}
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
void TransitionsAccessor::SetMigrationTarget(Map migration_target) {
// We only cache the migration target for maps with empty transitions for GC's
// sake.
if (encoding() != kUninitialized) return;
DCHECK(map_->is_deprecated());
map_->set_raw_transitions(MaybeObject::FromObject(migration_target));
MarkNeedsReload();
}
Map TransitionsAccessor::GetMigrationTarget() {
if (encoding() == kMigrationTarget) {
return map_->raw_transitions()->cast<Map>();
}
return Map();
}
void TransitionArray::Zap(Isolate* isolate) {
MemsetPointer(ObjectSlot(data_start() + kPrototypeTransitionsIndex),
ReadOnlyRoots(isolate).the_hole_value(),
length() - kPrototypeTransitionsIndex);
SetNumberOfTransitions(0);
}
void TransitionsAccessor::ReplaceTransitions(MaybeObject new_transitions) {
if (encoding() == kFullTransitionArray) {
TransitionArray old_transitions = transitions();
#if DEBUG
CheckNewTransitionsAreConsistent(
old_transitions, new_transitions->GetHeapObjectAssumeStrong());
DCHECK(old_transitions != new_transitions->GetHeapObjectAssumeStrong());
#endif
// 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.
old_transitions->Zap(isolate_);
}
map_->set_raw_transitions(new_transitions);
MarkNeedsReload();
}
void TransitionsAccessor::SetPrototypeTransitions(
Handle<WeakFixedArray> proto_transitions) {
EnsureHasFullTransitionArray();
transitions()->SetPrototypeTransitions(*proto_transitions);
}
void TransitionsAccessor::EnsureHasFullTransitionArray() {
if (encoding() == kFullTransitionArray) return;
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
int nof =
(encoding() == kUninitialized || encoding() == kMigrationTarget) ? 0 : 1;
Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(nof);
Reload(); // Reload after possible GC.
if (nof == 1) {
if (encoding() == kUninitialized) {
// If allocation caused GC and cleared the target, trim the new array.
result->SetNumberOfTransitions(0);
} else {
// Otherwise populate the new array.
Handle<Map> target(GetSimpleTransition(), isolate_);
Name key = GetSimpleTransitionKey(*target);
result->Set(0, key, HeapObjectReference::Weak(*target));
}
}
ReplaceTransitions(MaybeObject::FromObject(*result));
Reload(); // Reload after replacing transitions.
}
void TransitionsAccessor::TraverseTransitionTreeInternal(
TraverseCallback callback, void* data, DisallowHeapAllocation* no_gc) {
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
Reland "Create a fast path to get migration target when updating map" This is a reland of 6ec90ecee2817e6c6c9c91d8387dd0d55ba011c8 Original change's description: > Reland "Create a fast path to get migration target when updating map" > > This is a reland of c285380ca8b7ef93c7dc1558bb26f5c51a6af0ea > > 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/1270261 > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#56588} Change-Id: Iad23455f66c83340430370be6670bac493c638a1 Reviewed-on: https://chromium-review.googlesource.com/c/1309305 Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57705}
2018-11-20 05:54:21 +00:00
case kMigrationTarget:
break;
case kWeakRef: {
Map simple_target =
Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
TransitionsAccessor(isolate_, simple_target, no_gc)
.TraverseTransitionTreeInternal(callback, data, no_gc);
break;
}
case kFullTransitionArray: {
if (transitions()->HasPrototypeTransitions()) {
WeakFixedArray proto_trans = transitions()->GetPrototypeTransitions();
int length = TransitionArray::NumberOfPrototypeTransitions(proto_trans);
for (int i = 0; i < length; ++i) {
int index = TransitionArray::kProtoTransitionHeaderSize + i;
MaybeObject target = proto_trans->Get(index);
HeapObject* heap_object;
if (target->GetHeapObjectIfWeak(&heap_object)) {
TransitionsAccessor(isolate_, Map::cast(heap_object), no_gc)
.TraverseTransitionTreeInternal(callback, data, no_gc);
} else {
DCHECK(target->IsCleared());
}
}
}
for (int i = 0; i < transitions()->number_of_transitions(); ++i) {
TransitionsAccessor(isolate_, transitions()->GetTarget(i), no_gc)
.TraverseTransitionTreeInternal(callback, data, no_gc);
}
break;
}
}
callback(map_, data);
}
#ifdef DEBUG
void TransitionsAccessor::CheckNewTransitionsAreConsistent(
TransitionArray old_transitions, Object* transitions) {
// This function only handles full transition arrays.
DCHECK_EQ(kFullTransitionArray, encoding());
TransitionArray new_transitions = TransitionArray::cast(transitions);
for (int i = 0; i < old_transitions->number_of_transitions(); i++) {
Map target = old_transitions->GetTarget(i);
if (target->instance_descriptors() == map_->instance_descriptors()) {
Name key = old_transitions->GetKey(i);
int new_target_index;
if (IsSpecialTransition(ReadOnlyRoots(isolate_), key)) {
new_target_index = new_transitions->SearchSpecial(Symbol::cast(key));
} else {
PropertyDetails details = GetTargetDetails(key, target);
new_target_index =
new_transitions->Search(details.kind(), key, details.attributes());
}
DCHECK_NE(TransitionArray::kNotFound, new_target_index);
DCHECK_EQ(target, new_transitions->GetTarget(new_target_index));
}
}
}
#endif
// Private non-static helper functions (operating on full transition arrays).
int TransitionArray::SearchDetails(int transition, PropertyKind kind,
PropertyAttributes attributes,
int* out_insertion_index) {
int nof_transitions = number_of_transitions();
DCHECK(transition < nof_transitions);
Name key = GetKey(transition);
for (; transition < nof_transitions && GetKey(transition) == key;
transition++) {
Map target = GetTarget(transition);
PropertyDetails target_details =
TransitionsAccessor::GetTargetDetails(key, target);
int cmp = CompareDetails(kind, attributes, target_details.kind(),
target_details.attributes());
if (cmp == 0) {
return transition;
} else if (cmp < 0) {
break;
}
}
if (out_insertion_index != nullptr) *out_insertion_index = transition;
return kNotFound;
}
int TransitionArray::Search(PropertyKind kind, Name name,
PropertyAttributes attributes,
int* out_insertion_index) {
int transition = SearchName(name, out_insertion_index);
if (transition == kNotFound) return kNotFound;
return SearchDetails(transition, kind, attributes, out_insertion_index);
}
void TransitionArray::Sort() {
DisallowHeapAllocation no_gc;
// In-place insertion sort.
int length = number_of_transitions();
ReadOnlyRoots roots = GetReadOnlyRoots();
for (int i = 1; i < length; i++) {
Name key = GetKey(i);
MaybeObject target = GetRawTarget(i);
PropertyKind kind = kData;
PropertyAttributes attributes = NONE;
if (!TransitionsAccessor::IsSpecialTransition(roots, key)) {
Map target_map = TransitionsAccessor::GetTargetFromRaw(target);
PropertyDetails details =
TransitionsAccessor::GetTargetDetails(key, target_map);
kind = details.kind();
attributes = details.attributes();
}
int j;
for (j = i - 1; j >= 0; j--) {
Name temp_key = GetKey(j);
MaybeObject temp_target = GetRawTarget(j);
PropertyKind temp_kind = kData;
PropertyAttributes temp_attributes = NONE;
if (!TransitionsAccessor::IsSpecialTransition(roots, temp_key)) {
Map temp_target_map =
TransitionsAccessor::GetTargetFromRaw(temp_target);
PropertyDetails details =
TransitionsAccessor::GetTargetDetails(temp_key, temp_target_map);
temp_kind = details.kind();
temp_attributes = details.attributes();
}
int cmp =
CompareKeys(temp_key, temp_key->Hash(), temp_kind, temp_attributes,
key, key->Hash(), kind, attributes);
if (cmp > 0) {
SetKey(j + 1, temp_key);
SetRawTarget(j + 1, temp_target);
} else {
break;
}
}
SetKey(j + 1, key);
SetRawTarget(j + 1, target);
}
DCHECK(IsSortedNoDuplicates());
}
} // namespace internal
} // namespace v8