Optimize ClearNonLiveReferences: do not compact prototype transitions in GC.
BUG=chromium:554488 LOG=NO Review URL: https://codereview.chromium.org/1470773003 Cr-Commit-Position: refs/heads/master@{#32272}
This commit is contained in:
parent
e8bfedab96
commit
7a21cdafaf
@ -2261,10 +2261,7 @@ void MarkCompactCollector::ClearNonLiveReferences() {
|
|||||||
Map* map = Map::cast(obj);
|
Map* map = Map::cast(obj);
|
||||||
if (!map->CanTransition()) continue;
|
if (!map->CanTransition()) continue;
|
||||||
MarkBit map_mark = Marking::MarkBitFrom(map);
|
MarkBit map_mark = Marking::MarkBitFrom(map);
|
||||||
bool alive = Marking::IsBlackOrGrey(map_mark);
|
if (Marking::IsWhite(map_mark)) {
|
||||||
if (alive) {
|
|
||||||
ClearNonLivePrototypeTransitions(map);
|
|
||||||
} else {
|
|
||||||
ClearNonLiveMapTransitions(map);
|
ClearNonLiveMapTransitions(map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2304,39 +2301,6 @@ void MarkCompactCollector::MarkDependentCodeListForDeoptimization(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
|
|
||||||
FixedArray* prototype_transitions =
|
|
||||||
TransitionArray::GetPrototypeTransitions(map);
|
|
||||||
int number_of_transitions =
|
|
||||||
TransitionArray::NumberOfPrototypeTransitions(prototype_transitions);
|
|
||||||
|
|
||||||
const int header = TransitionArray::kProtoTransitionHeaderSize;
|
|
||||||
int new_number_of_transitions = 0;
|
|
||||||
for (int i = 0; i < number_of_transitions; i++) {
|
|
||||||
Object* cell = prototype_transitions->get(header + i);
|
|
||||||
if (!WeakCell::cast(cell)->cleared()) {
|
|
||||||
if (new_number_of_transitions != i) {
|
|
||||||
prototype_transitions->set(header + new_number_of_transitions, cell);
|
|
||||||
Object** slot = prototype_transitions->RawFieldOfElementAt(
|
|
||||||
header + new_number_of_transitions);
|
|
||||||
RecordSlot(prototype_transitions, slot, cell);
|
|
||||||
}
|
|
||||||
new_number_of_transitions++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_number_of_transitions != number_of_transitions) {
|
|
||||||
TransitionArray::SetNumberOfPrototypeTransitions(prototype_transitions,
|
|
||||||
new_number_of_transitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill slots that became free with undefined value.
|
|
||||||
for (int i = new_number_of_transitions; i < number_of_transitions; i++) {
|
|
||||||
prototype_transitions->set_undefined(header + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map) {
|
void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map) {
|
||||||
Object* potential_parent = map->GetBackPointer();
|
Object* potential_parent = map->GetBackPointer();
|
||||||
if (!potential_parent->IsMap()) return;
|
if (!potential_parent->IsMap()) return;
|
||||||
|
@ -656,7 +656,6 @@ class MarkCompactCollector {
|
|||||||
// Map transitions from a live map to a dead map must be killed.
|
// Map transitions from a live map to a dead map must be killed.
|
||||||
// We replace them with a null descriptor, with the same key.
|
// We replace them with a null descriptor, with the same key.
|
||||||
void ClearNonLiveReferences();
|
void ClearNonLiveReferences();
|
||||||
void ClearNonLivePrototypeTransitions(Map* map);
|
|
||||||
void ClearNonLiveMapTransitions(Map* map);
|
void ClearNonLiveMapTransitions(Map* map);
|
||||||
void ClearMapTransitions(Map* map, Map* dead_transition);
|
void ClearMapTransitions(Map* map, Map* dead_transition);
|
||||||
bool ClearMapBackPointer(Map* map);
|
bool ClearMapBackPointer(Map* map);
|
||||||
|
@ -233,6 +233,61 @@ bool TransitionArray::CanHaveMoreTransitions(Handle<Map> map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool TransitionArray::CompactPrototypeTransitionArray(FixedArray* 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++) {
|
||||||
|
Object* cell = array->get(header + i);
|
||||||
|
if (!WeakCell::cast(cell)->cleared()) {
|
||||||
|
if (new_number_of_transitions != i) {
|
||||||
|
array->set(header + new_number_of_transitions, cell);
|
||||||
|
}
|
||||||
|
new_number_of_transitions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fill slots that became free with undefined value.
|
||||||
|
for (int i = new_number_of_transitions; i < number_of_transitions; i++) {
|
||||||
|
array->set_undefined(header + i);
|
||||||
|
}
|
||||||
|
if (number_of_transitions != new_number_of_transitions) {
|
||||||
|
SetNumberOfPrototypeTransitions(array, new_number_of_transitions);
|
||||||
|
}
|
||||||
|
return new_number_of_transitions < number_of_transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
Handle<FixedArray> TransitionArray::GrowPrototypeTransitionArray(
|
||||||
|
Handle<FixedArray> 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()->CopyFixedArrayAndGrow(array, grow_by);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
int TransitionArray::NumberOfPrototypeTransitionsForTest(Map* map) {
|
||||||
|
FixedArray* transitions = GetPrototypeTransitions(map);
|
||||||
|
CompactPrototypeTransitionArray(transitions);
|
||||||
|
return TransitionArray::NumberOfPrototypeTransitions(transitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void TransitionArray::PutPrototypeTransition(Handle<Map> map,
|
void TransitionArray::PutPrototypeTransition(Handle<Map> map,
|
||||||
Handle<Object> prototype,
|
Handle<Object> prototype,
|
||||||
@ -252,23 +307,16 @@ void TransitionArray::PutPrototypeTransition(Handle<Map> map,
|
|||||||
int transitions = NumberOfPrototypeTransitions(*cache) + 1;
|
int transitions = NumberOfPrototypeTransitions(*cache) + 1;
|
||||||
|
|
||||||
if (transitions > capacity) {
|
if (transitions > capacity) {
|
||||||
// Grow array by factor 2 up to MaxCachedPrototypeTransitions.
|
// Grow the array if compacting it doesn't free space.
|
||||||
int new_capacity = Min(kMaxCachedPrototypeTransitions, transitions * 2);
|
if (!CompactPrototypeTransitionArray(*cache)) {
|
||||||
if (new_capacity == capacity) return;
|
if (capacity == kMaxCachedPrototypeTransitions) return;
|
||||||
int grow_by = new_capacity - capacity;
|
cache = GrowPrototypeTransitionArray(cache, 2 * transitions,
|
||||||
|
map->GetIsolate());
|
||||||
Isolate* isolate = map->GetIsolate();
|
|
||||||
cache = isolate->factory()->CopyFixedArrayAndGrow(cache, grow_by);
|
|
||||||
if (capacity < 0) {
|
|
||||||
// There was no prototype transitions array before, so the size
|
|
||||||
// couldn't be copied. Initialize it explicitly.
|
|
||||||
SetNumberOfPrototypeTransitions(*cache, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetPrototypeTransitions(map, cache);
|
SetPrototypeTransitions(map, cache);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reload number of transitions as GC might shrink them.
|
// Reload number of transitions as they might have been compacted.
|
||||||
int last = NumberOfPrototypeTransitions(*cache);
|
int last = NumberOfPrototypeTransitions(*cache);
|
||||||
int entry = header + last;
|
int entry = header + last;
|
||||||
|
|
||||||
@ -387,25 +435,9 @@ void TransitionArray::ReplaceTransitions(Handle<Map> map,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ZapPrototypeTransitions(Object* raw_transitions) {
|
|
||||||
DCHECK(TransitionArray::IsFullTransitionArray(raw_transitions));
|
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
|
||||||
if (!transitions->HasPrototypeTransitions()) return;
|
|
||||||
FixedArray* proto_transitions = transitions->GetPrototypeTransitions();
|
|
||||||
MemsetPointer(proto_transitions->data_start(),
|
|
||||||
proto_transitions->GetHeap()->the_hole_value(),
|
|
||||||
proto_transitions->length());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void TransitionArray::SetPrototypeTransitions(
|
void TransitionArray::SetPrototypeTransitions(
|
||||||
Handle<Map> map, Handle<FixedArray> proto_transitions) {
|
Handle<Map> map, Handle<FixedArray> proto_transitions) {
|
||||||
EnsureHasFullTransitionArray(map);
|
EnsureHasFullTransitionArray(map);
|
||||||
if (Heap::ShouldZapGarbage()) {
|
|
||||||
Object* raw_transitions = map->raw_transitions();
|
|
||||||
DCHECK(raw_transitions != *proto_transitions);
|
|
||||||
ZapPrototypeTransitions(raw_transitions);
|
|
||||||
}
|
|
||||||
TransitionArray* transitions = TransitionArray::cast(map->raw_transitions());
|
TransitionArray* transitions = TransitionArray::cast(map->raw_transitions());
|
||||||
transitions->SetPrototypeTransitions(*proto_transitions);
|
transitions->SetPrototypeTransitions(*proto_transitions);
|
||||||
}
|
}
|
||||||
@ -444,10 +476,12 @@ void TransitionArray::TraverseTransitionTreeInternal(Map* map,
|
|||||||
for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) {
|
for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) {
|
||||||
int index = TransitionArray::kProtoTransitionHeaderSize + i;
|
int index = TransitionArray::kProtoTransitionHeaderSize + i;
|
||||||
WeakCell* cell = WeakCell::cast(proto_trans->get(index));
|
WeakCell* cell = WeakCell::cast(proto_trans->get(index));
|
||||||
|
if (!cell->cleared()) {
|
||||||
TraverseTransitionTreeInternal(Map::cast(cell->value()), callback,
|
TraverseTransitionTreeInternal(Map::cast(cell->value()), callback,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
|
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
|
||||||
TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
|
TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,7 @@ class TransitionArray: public FixedArray {
|
|||||||
Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
|
Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
|
||||||
return Smi::cast(raw)->value();
|
return Smi::cast(raw)->value();
|
||||||
}
|
}
|
||||||
|
static int NumberOfPrototypeTransitionsForTest(Map* map);
|
||||||
|
|
||||||
static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions,
|
static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions,
|
||||||
int value);
|
int value);
|
||||||
@ -272,6 +273,11 @@ class TransitionArray: public FixedArray {
|
|||||||
static void SetPrototypeTransitions(Handle<Map> map,
|
static void SetPrototypeTransitions(Handle<Map> map,
|
||||||
Handle<FixedArray> proto_transitions);
|
Handle<FixedArray> proto_transitions);
|
||||||
|
|
||||||
|
static bool CompactPrototypeTransitionArray(FixedArray* array);
|
||||||
|
|
||||||
|
static Handle<FixedArray> GrowPrototypeTransitionArray(
|
||||||
|
Handle<FixedArray> array, int new_capacity, Isolate* isolate);
|
||||||
|
|
||||||
// Compares two tuples <key, kind, attributes>, returns -1 if
|
// Compares two tuples <key, kind, attributes>, returns -1 if
|
||||||
// tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise.
|
// tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise.
|
||||||
static inline int CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
static inline int CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
||||||
|
@ -2620,12 +2620,6 @@ TEST(InstanceOfStubWriteBarrier) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int NumberOfProtoTransitions(Map* map) {
|
|
||||||
return TransitionArray::NumberOfPrototypeTransitions(
|
|
||||||
TransitionArray::GetPrototypeTransitions(map));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(PrototypeTransitionClearing) {
|
TEST(PrototypeTransitionClearing) {
|
||||||
if (FLAG_never_compact) return;
|
if (FLAG_never_compact) return;
|
||||||
CcTest::InitializeVM();
|
CcTest::InitializeVM();
|
||||||
@ -2639,7 +2633,8 @@ TEST(PrototypeTransitionClearing) {
|
|||||||
v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
|
v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
|
||||||
CcTest::global()->Get(ctx, v8_str("base")).ToLocalChecked()));
|
CcTest::global()->Get(ctx, v8_str("base")).ToLocalChecked()));
|
||||||
|
|
||||||
int initialTransitions = NumberOfProtoTransitions(baseObject->map());
|
int initialTransitions =
|
||||||
|
TransitionArray::NumberOfPrototypeTransitionsForTest(baseObject->map());
|
||||||
|
|
||||||
CompileRun(
|
CompileRun(
|
||||||
"var live = [];"
|
"var live = [];"
|
||||||
@ -2651,12 +2646,14 @@ TEST(PrototypeTransitionClearing) {
|
|||||||
"}");
|
"}");
|
||||||
|
|
||||||
// Verify that only dead prototype transitions are cleared.
|
// Verify that only dead prototype transitions are cleared.
|
||||||
CHECK_EQ(initialTransitions + 10,
|
CHECK_EQ(
|
||||||
NumberOfProtoTransitions(baseObject->map()));
|
initialTransitions + 10,
|
||||||
|
TransitionArray::NumberOfPrototypeTransitionsForTest(baseObject->map()));
|
||||||
CcTest::heap()->CollectAllGarbage();
|
CcTest::heap()->CollectAllGarbage();
|
||||||
const int transitions = 10 - 3;
|
const int transitions = 10 - 3;
|
||||||
CHECK_EQ(initialTransitions + transitions,
|
CHECK_EQ(
|
||||||
NumberOfProtoTransitions(baseObject->map()));
|
initialTransitions + transitions,
|
||||||
|
TransitionArray::NumberOfPrototypeTransitionsForTest(baseObject->map()));
|
||||||
|
|
||||||
// Verify that prototype transitions array was compacted.
|
// Verify that prototype transitions array was compacted.
|
||||||
FixedArray* trans =
|
FixedArray* trans =
|
||||||
|
Loading…
Reference in New Issue
Block a user