Commit 151526a8 authored by Jakob Gruber's avatar Jakob Gruber Committed by V8 LUCI CQ

[transitions] Change TraverseTransitionTree to iterative preorder DFS

Prior to this CL, TraverseTransitionTree was a recursive post-order
visitor. This led to stack overflows for deep transition trees.

This CL changes to an iterative DFS algorithm instead. Since no user
seems to rely on the visitation order, it was changed from postorder to
preorder for ease of implementation.

Bug: chromium:1224935
Change-Id: Ibda199422fb20fb4470c5c68947e0afbd9a5e596
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3084366
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76195}
parent 39045667
...@@ -215,26 +215,32 @@ void TransitionsAccessor::Reload() { ...@@ -215,26 +215,32 @@ void TransitionsAccessor::Reload() {
int TransitionsAccessor::Capacity() { return transitions().Capacity(); } int TransitionsAccessor::Capacity() { return transitions().Capacity(); }
void TransitionsAccessor::Initialize() { // static
raw_transitions_ = map_.raw_transitions(isolate_, kAcquireLoad); TransitionsAccessor::Encoding TransitionsAccessor::GetEncoding(
Isolate* isolate, MaybeObject raw_transitions) {
HeapObject heap_object; HeapObject heap_object;
if (raw_transitions_->IsSmi() || raw_transitions_->IsCleared()) { if (raw_transitions->IsSmi() || raw_transitions->IsCleared()) {
encoding_ = kUninitialized; return kUninitialized;
} else if (raw_transitions_->IsWeak()) { } else if (raw_transitions->IsWeak()) {
encoding_ = kWeakRef; return kWeakRef;
} else if (raw_transitions_->GetHeapObjectIfStrong(isolate_, &heap_object)) { } else if (raw_transitions->GetHeapObjectIfStrong(isolate, &heap_object)) {
if (heap_object.IsTransitionArray()) { if (heap_object.IsTransitionArray()) {
encoding_ = kFullTransitionArray; return kFullTransitionArray;
} else if (heap_object.IsPrototypeInfo()) { } else if (heap_object.IsPrototypeInfo()) {
encoding_ = kPrototypeInfo; return kPrototypeInfo;
} else { } else {
DCHECK(map_.is_deprecated());
DCHECK(heap_object.IsMap()); DCHECK(heap_object.IsMap());
encoding_ = kMigrationTarget; return kMigrationTarget;
} }
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }
}
void TransitionsAccessor::Initialize() {
raw_transitions_ = map_.raw_transitions(isolate_, kAcquireLoad);
encoding_ = GetEncoding(isolate_, raw_transitions_);
DCHECK_IMPLIES(encoding_ == kMigrationTarget, map_.is_deprecated());
#if DEBUG #if DEBUG
needs_reload_ = false; needs_reload_ = false;
#endif #endif
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/objects/transitions.h" #include "src/objects/transitions.h"
#include "src/base/small-vector.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/objects/transitions-inl.h" #include "src/objects/transitions-inl.h"
#include "src/utils/utils.h" #include "src/utils/utils.h"
...@@ -512,45 +513,59 @@ void TransitionsAccessor::EnsureHasFullTransitionArray() { ...@@ -512,45 +513,59 @@ void TransitionsAccessor::EnsureHasFullTransitionArray() {
} }
void TransitionsAccessor::TraverseTransitionTreeInternal( void TransitionsAccessor::TraverseTransitionTreeInternal(
TraverseCallback callback, DisallowGarbageCollection* no_gc) { const TraverseCallback& callback, DisallowGarbageCollection* no_gc) {
switch (encoding()) { // Mostly arbitrary but more than enough to run the test suite in static
// memory.
static constexpr int kStaticStackSize = 16;
base::SmallVector<Map, kStaticStackSize> stack;
stack.emplace_back(map_);
// Pre-order iterative depth-first-search.
while (!stack.empty()) {
Map current_map = stack.back();
stack.pop_back();
callback(current_map);
MaybeObject raw_transitions =
current_map.raw_transitions(isolate_, kAcquireLoad);
Encoding encoding = GetEncoding(isolate_, raw_transitions);
switch (encoding) {
case kPrototypeInfo: case kPrototypeInfo:
case kUninitialized: case kUninitialized:
case kMigrationTarget: case kMigrationTarget:
break; break;
case kWeakRef: { case kWeakRef: {
Map simple_target = stack.emplace_back(
Map::cast(raw_transitions_->GetHeapObjectAssumeWeak()); Map::cast(raw_transitions->GetHeapObjectAssumeWeak()));
TransitionsAccessor(isolate_, simple_target, no_gc, concurrent_access_)
.TraverseTransitionTreeInternal(callback, no_gc);
break; break;
} }
case kFullTransitionArray: { case kFullTransitionArray: {
if (transitions().HasPrototypeTransitions()) { TransitionArray transitions =
WeakFixedArray proto_trans = transitions().GetPrototypeTransitions(); TransitionArray::cast(raw_transitions->GetHeapObjectAssumeStrong());
int length = TransitionArray::NumberOfPrototypeTransitions(proto_trans); if (transitions.HasPrototypeTransitions()) {
WeakFixedArray proto_trans = transitions.GetPrototypeTransitions();
int length =
TransitionArray::NumberOfPrototypeTransitions(proto_trans);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
int index = TransitionArray::kProtoTransitionHeaderSize + i; int index = TransitionArray::kProtoTransitionHeaderSize + i;
MaybeObject target = proto_trans.Get(index); MaybeObject target = proto_trans.Get(index);
HeapObject heap_object; HeapObject heap_object;
if (target->GetHeapObjectIfWeak(&heap_object)) { if (target->GetHeapObjectIfWeak(&heap_object)) {
TransitionsAccessor(isolate_, Map::cast(heap_object), no_gc, stack.emplace_back(Map::cast(heap_object));
concurrent_access_)
.TraverseTransitionTreeInternal(callback, no_gc);
} else { } else {
DCHECK(target->IsCleared()); DCHECK(target->IsCleared());
} }
} }
} }
for (int i = 0; i < transitions().number_of_transitions(); ++i) { for (int i = 0; i < transitions.number_of_transitions(); ++i) {
TransitionsAccessor(isolate_, transitions().GetTarget(i), no_gc, stack.emplace_back(transitions.GetTarget(i));
concurrent_access_)
.TraverseTransitionTreeInternal(callback, no_gc);
} }
break; break;
} }
} }
callback(map_); }
} }
#ifdef DEBUG #ifdef DEBUG
......
...@@ -107,8 +107,8 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -107,8 +107,8 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
// ===== ITERATION ===== // ===== ITERATION =====
using TraverseCallback = std::function<void(Map)>; using TraverseCallback = std::function<void(Map)>;
// Traverse the transition tree in postorder. // Traverse the transition tree in preorder.
void TraverseTransitionTree(TraverseCallback callback) { void TraverseTransitionTree(const TraverseCallback& callback) {
// Make sure that we do not allocate in the callback. // Make sure that we do not allocate in the callback.
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
base::SharedMutexGuardIf<base::kShared> scope( base::SharedMutexGuardIf<base::kShared> scope(
...@@ -175,6 +175,9 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -175,6 +175,9 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
friend class third_party_heap::Impl; friend class third_party_heap::Impl;
friend class TransitionArray; friend class TransitionArray;
static inline Encoding GetEncoding(Isolate* isolate,
MaybeObject raw_transitions);
inline PropertyDetails GetSimpleTargetDetails(Map transition); inline PropertyDetails GetSimpleTargetDetails(Map transition);
static inline Name GetSimpleTransitionKey(Map transition); static inline Name GetSimpleTransitionKey(Map transition);
...@@ -200,7 +203,7 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -200,7 +203,7 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
void SetPrototypeTransitions(Handle<WeakFixedArray> proto_transitions); void SetPrototypeTransitions(Handle<WeakFixedArray> proto_transitions);
WeakFixedArray GetPrototypeTransitions(); WeakFixedArray GetPrototypeTransitions();
void TraverseTransitionTreeInternal(TraverseCallback callback, void TraverseTransitionTreeInternal(const TraverseCallback& callback,
DisallowGarbageCollection* no_gc); DisallowGarbageCollection* no_gc);
Isolate* isolate_; Isolate* isolate_;
......
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