Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Unified Diff: src/objects.cc

Issue 9252007: Refactored iterative map traversal. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Filled out the skeleton Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/objects.cc
diff --git a/src/objects.cc b/src/objects.cc
index 0941e792f04e98f3391c5a8757da74902d74e21e..c4f87c4b91a738edb00d54fd7e64e1650b404293 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -4935,75 +4935,190 @@ void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
}
-void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
- // Traverse the transition tree without using a stack. We do this by
- // reversing the pointers in the maps and descriptor arrays.
- Map* current = this;
- Map* meta_map = GetHeap()->meta_map();
- Object** map_or_index_field = NULL;
- while (current != meta_map) {
- DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
- *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset));
- if (!d->IsEmpty()) {
- FixedArray* contents = reinterpret_cast<FixedArray*>(
- d->get(DescriptorArray::kContentArrayIndex));
- map_or_index_field = RawField(contents, HeapObject::kMapOffset);
- Object* map_or_index = *map_or_index_field;
- bool map_done = true; // Controls a nested continue statement.
- for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
- i < contents->length();
- i += 2) {
- PropertyDetails details(Smi::cast(contents->get(i + 1)));
- if (details.IsTransition()) {
- // Found a map in the transition array. We record our progress in
- // the transition array by recording the current map in the map field
- // of the next map and recording the index in the transition array in
- // the map field of the array.
- Map* next = Map::cast(contents->get(i));
- next->set_map_no_write_barrier(current);
- *map_or_index_field = Smi::FromInt(i + 2);
- current = next;
- map_done = false;
- break;
- }
- }
- if (!map_done) continue;
- } else {
- map_or_index_field = NULL;
- }
- // That was the regular transitions, now for the prototype transitions.
- FixedArray* prototype_transitions =
- current->unchecked_prototype_transitions();
- Object** proto_map_or_index_field =
- RawField(prototype_transitions, HeapObject::kMapOffset);
- Object* map_or_index = *proto_map_or_index_field;
- const int start = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
- int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : start;
- if (i < prototype_transitions->length()) {
- // Found a map in the prototype transition array. Record progress in
- // an analogous way to the regular transitions array above.
- Object* perhaps_map = prototype_transitions->get(i);
- if (perhaps_map->IsMap()) {
- Map* next = Map::cast(perhaps_map);
- next->set_map_no_write_barrier(current);
- *proto_map_or_index_field =
- Smi::FromInt(i + kProtoTransitionElementsPerEntry);
- current = next;
- continue;
+// An iterator over all map transitions in an descriptor array, reusing the map
+// field of the contens array while it is running.
+class IterableDescriptorArray : public DescriptorArray {
+ public:
+ void Start() {
+ ASSERT(!IsRunning());
+ if (HasContentArray()) *ContentHeader() = Smi::FromInt(0);
+ }
+
+ bool IsRunning() {
Vyacheslav Egorov (Chromium) 2012/01/20 12:04:54 I find the name confusing because descriptor itsel
Sven Panne 2012/01/20 13:15:33 If we really wanted to be clean, we should introdu
Vyacheslav Egorov (Chromium) 2012/01/20 13:59:50 IsIterating sounds fine.
+ return HasContentArray() && (*ContentHeader())->IsSmi();
+ }
+
+ Map* Next() {
+ ASSERT(IsRunning());
+ FixedArray* contents = ContentArray();
+ int index = Smi::cast(*ContentHeader())->value();
+ while (index < contents->length()) {
+ int next_index = index + 2;
+ PropertyDetails details(Smi::cast(contents->get(index + 1)));
+ if (details.IsTransition()) {
+ *ContentHeader() = Smi::FromInt(next_index);
+ return static_cast<Map*>(contents->get(index));
}
+ index = next_index;
+ }
+ *ContentHeader() = GetHeap()->fixed_array_map();
+ return NULL;
+ }
+
+ private:
+ bool HasContentArray() { return length() > kContentArrayIndex; }
+
+ FixedArray* ContentArray() {
+ return static_cast<FixedArray*>(get(kContentArrayIndex));
+ }
+
+ Object** ContentHeader() {
+ return RawField(ContentArray(), kMapOffset);
+ }
+};
+
+
+// An iterator over all prototype transitions, reusing the map field of the
+// underlying array while it is running.
+class IterablePrototypeTransitions : public FixedArray {
Vyacheslav Egorov (Chromium) 2012/01/20 12:04:54 I think parts IterableDescriptorArray & IterablePr
Sven Panne 2012/01/20 13:15:33 There are several reasons why I would prefer to av
Vyacheslav Egorov (Chromium) 2012/01/20 13:59:50 * Yes, it is a pure accident that both are fixed a
Sven Panne 2012/01/20 14:54:50 What code would actually be shared? The structure
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 I outlined what I think can be shared in my intrus
+ public:
+ void Start() {
+ ASSERT(!IsRunning());
+ if (HasTransitions()) *Header() = Smi::FromInt(0);
+ }
+
+ bool IsRunning() {
+ return HasTransitions() && (*Header())->IsSmi();
+ }
+
+ Map* Next() {
+ ASSERT(IsRunning());
+ int index = Smi::cast(*Header())->value();
+ if (index < NumberOfTransitions()) {
+ *Header() = Smi::FromInt(index + 1);
+ return GetTransition(index);
+ }
+ *Header() = GetHeap()->fixed_array_map();
+ return NULL;
+ }
+
+ private:
+ bool HasTransitions() { return length() >= Map::kProtoTransitionHeaderSize; }
+
+ Object** Header() {
+ return RawField(this, kMapOffset);
+ }
+
+ int NumberOfTransitions() {
+ return Smi::cast(get(Map::kProtoTransitionNumberOfEntriesOffset))->value();
+ }
+
+ Map* GetTransition(int transitionNumber) {
+ return Map::cast(
+ get(Map::kProtoTransitionHeaderSize +
+ Map::kProtoTransitionMapOffset +
+ transitionNumber * Map::kProtoTransitionElementsPerEntry));
+ }
+};
+
+
+// To traverse the transition tree iteratively, we have to store two kinds of
+// information in a map: The parent map in the traversal and which children of a
+// node have already been visited. To do this without additional memory, we
+// temporarily reuse two maps with known values:
+//
+// (1) The map of the map temporarily holds the parent, and is restored to the
+// meta map afterwards.
+//
+// (2) The info which children have already been visited depends on which part
+// of the map we currently iterate:
+//
+// (a) If we currently follow normal map transitions, we temporarily store
+// the current index in the map of the FixedArray of the desciptor
+// array's contents, and restore it to the fixed array map afterwards.
+// Note that a single descriptor can have 0, 1, or 2 transitions.
+//
+// (b) If we currently follow prototype transitions, we temporarily store
+// the current index in the map of the FixedArray holding the prototype
+// transitions, and restore it to the fixed array map afterwards.
+//
+// Note that the child iterator is just a concatenation of two iterators: One
+// iterating over map transitions and one iterating over prototype transisitons.
+class IterableMap : public Map {
+ public:
+ // Record the parent in the traversal within this map. Note that this destroys
+ // this map's map!
+ void setParent(IterableMap* parent) { set_map_no_write_barrier(parent); }
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 naming convention: first letter capital
+
+ // Reset the current map's map, returning the parent previously stored in it.
+ IterableMap* getAndResetParent() {
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 naming convention: first letter capital
+ IterableMap* old_parent = static_cast<IterableMap*>(map());
+ set_map_no_write_barrier(GetHeap()->meta_map());
+ return old_parent;
+ }
+
+ // Start iterating over this map's children, possibly destroying a FixedArray
+ // map (see explanation above).
+ void childIteratorStart() {
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 naming convention: first letter capital
+ iterable_descriptor_array()->Start();
+ iterable_prototype_transitions()->Start();
+ }
+
+ // If we have an unvisited child map, return that one and advance. If we have
+ // none, return NULL and reset any destroyed FixedArray maps.
+ IterableMap* childIteratorNext() {
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 naming convention: first letter capital
+ if (iterable_descriptor_array()->IsRunning()) {
+ Map* next = iterable_descriptor_array()->Next();
+ if (next != NULL) return static_cast<IterableMap*>(next);
}
- *proto_map_or_index_field = GetHeap()->fixed_array_map();
- if (map_or_index_field != NULL) {
- *map_or_index_field = GetHeap()->fixed_array_map();
+ if (iterable_prototype_transitions()->IsRunning()) {
+ Map* next = iterable_prototype_transitions()->Next();
+ if (next != NULL) return static_cast<IterableMap*>(next);
}
+ return NULL;
+ }
+
+ private:
+ IterableDescriptorArray* iterable_descriptor_array() {
+ return static_cast<IterableDescriptorArray*>(instance_descriptors());
+ }
+
+ IterablePrototypeTransitions* iterable_prototype_transitions() {
+ return static_cast<IterablePrototypeTransitions*>(
+ unchecked_prototype_transitions());
+ }
+};
+
- // The callback expects a map to have a real map as its map, so we save
- // the map field, which is being used to track the traversal and put the
- // correct map (the meta_map) in place while we do the callback.
- Map* prev = current->map();
- current->set_map_no_write_barrier(meta_map);
- callback(current, data);
- current = prev;
+// Traverse the transition tree in postorder without using the C++ stack. We can
+// deduce the algorithm below using the following steps:
+//
+// (1) Implement the recursive textbook algorithm for visiting a tree.
Vyacheslav Egorov (Chromium) 2012/01/20 12:04:54 I don't think these 4 steps make algorithm any cle
Sven Panne 2012/01/20 13:15:33 I can easily live without the comment, but this wo
Vyacheslav Egorov (Chromium) 2012/01/20 13:59:50 I agree that this comment is absolutely correct, a
Sven Panne 2012/01/20 14:54:50 Understanding the derivation of a non-trivial algo
+//
+// (2) Transform that into an iterative version using an explicit stack, where
+// each stack entry contains a pair of values: Which tree node do we
+// currently visit and info about which children of this node have already
+// been visited.
+//
+// (3) Use local variables for the top of stack pair.
+//
+// (4) Put the stack entry pair directly into the nodes, correctly resetting
+// any overwritten parts after the visit.
+void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
+ IterableMap* current = static_cast<IterableMap*>(this);
+ current->childIteratorStart();
+ while (true) {
Vyacheslav Egorov (Chromium) 2012/01/20 12:04:54 can it be changed into do { } while (current != me
Sven Panne 2012/01/20 13:15:33 I would prefer to leave it as it is, because using
Vyacheslav Egorov (Chromium) 2012/01/20 13:59:50 It would streamline control-flow (I would read thi
Sven Panne 2012/01/20 14:54:50 HasNext would complicate the currently easy iterat
Vyacheslav Egorov (Chromium) 2012/01/20 16:30:10 I consider loops with no special exits easier to r
+ IterableMap* child = current->childIteratorNext();
Vyacheslav Egorov (Chromium) 2012/01/20 12:04:54 I think it might be more clear to have descendInto
Sven Panne 2012/01/20 13:15:33 I think merging those calls together will make thi
Vyacheslav Egorov (Chromium) 2012/01/20 13:59:50 I have to agree that merging things together might
+ if (child != NULL) {
+ child->childIteratorStart();
+ child->setParent(current);
+ current = child;
+ } else {
+ IterableMap* parent = current->getAndResetParent();
+ callback(current, data);
+ if (current == this) break;
+ current = parent;
+ }
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698