Revision: 10651
Author: [email protected]
Date: Thu Feb 9 00:58:19 2012
Log: Improve GVN handling of ElementTransitions.
BUG=
TEST=
Review URL: https://chromiumcodereview.appspot.com/9141016
http://code.google.com/p/v8/source/detail?r=10651
Added:
/branches/bleeding_edge/test/mjsunit/elements-transition-hoisting.js
Modified:
/branches/bleeding_edge/src/hydrogen-instructions.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/hydrogen.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/elements-transition-hoisting.js
Thu Feb 9 00:58:19 2012
@@ -0,0 +1,168 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax --smi-only-arrays
+
+// Ensure that ElementsKind transitions in various situations are hoisted
(or
+// not hoisted) correctly, don't change the semantics programs and don't
trigger
+// deopt through hoisting in important situations.
+
+support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6));
+
+if (support_smi_only_arrays) {
+ print("Tests include smi-only arrays.");
+} else {
+ print("Tests do NOT include smi-only arrays.");
+}
+
+if (support_smi_only_arrays) {
+ // Make sure that a simple elements array transitions inside a loop
before
+ // stores to an array gets hoisted in a way that doesn't generate a
deopt in
+ // simple cases.}
+ function testDoubleConversion4(a) {
+ var object = new Object();
+ a[0] = 0;
+ var count = 3;
+ do {
+ a[0] = object;
+ } while (--count > 0);
+ }
+
+ testDoubleConversion4(new Array(5));
+ %OptimizeFunctionOnNextCall(testDoubleConversion4);
+ testDoubleConversion4(new Array(5));
+ testDoubleConversion4(new Array(5));
+ assertTrue(2 != %GetOptimizationStatus(testDoubleConversion4));
+
+ // Make sure that non-element related map checks that are not preceded by
+ // transitions in a loop still get hoisted in a way that doesn't
generate a
+ // deopt in simple cases.
+ function testExactMapHoisting(a) {
+ var object = new Object();
+ a.foo = 0;
+ a[0] = 0;
+ a[1] = 1;
+ var count = 3;
+ do {
+ a.foo = object; // This map check should be hoistable
+ a[1] = object;
+ result = a.foo == object && a[1] == object;
+ } while (--count > 0);
+ }
+
+ testExactMapHoisting(new Array(5));
+ %OptimizeFunctionOnNextCall(testExactMapHoisting);
+ testExactMapHoisting(new Array(5));
+ testExactMapHoisting(new Array(5));
+ assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting));
+
+ // Make sure that non-element related map checks do NOT get hoisted if
they
+ // depend on an elements transition before them and it's not possible to
hoist
+ // that transition.
+ function testExactMapHoisting2(a) {
+ var object = new Object();
+ a.foo = 0;
+ a[0] = 0;
+ a[1] = 1;
+ var count = 3;
+ do {
+ if (a.bar === undefined) {
+ a[1] = 2.5;
+ }
+ a.foo = object; // This map check should NOT be hoistable because it
+ // includes a check for the FAST_ELEMENTS map as
well as
+ // the FAST_DOUBLE_ELEMENTS map, which depends on the
+ // double transition above in the if, which cannot be
+ // hoisted.
+ } while (--count > 0);
+ }
+
+ testExactMapHoisting2(new Array(5));
+ %OptimizeFunctionOnNextCall(testExactMapHoisting2);
+ testExactMapHoisting2(new Array(5));
+ testExactMapHoisting2(new Array(5));
+ assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting2));
+
+ // Make sure that non-element related map checks do get hoisted if they
use
+ // the transitioned map for the check and all transitions that they
depend
+ // upon can hoisted, too.
+ function testExactMapHoisting3(a) {
+ var object = new Object();
+ a.foo = 0;
+ a[0] = 0;
+ a[1] = 1;
+ var count = 3;
+ do {
+ a[1] = 2.5;
+ a.foo = object; // This map check should be hoistable because all
elements
+ // transitions in the loop can also be hoisted.
+ } while (--count > 0);
+ }
+
+ var add_transition = new Array(5);
+ add_transition.foo = 0;
+ add_transition[0] = new Object(); // For FAST_ELEMENT transition to be
created
+ testExactMapHoisting3(new Array(5));
+ %OptimizeFunctionOnNextCall(testExactMapHoisting3);
+ testExactMapHoisting3(new Array(5));
+ testExactMapHoisting3(new Array(5));
+ assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting3));
+
+ function testDominatingTransitionHoisting1(a) {
+ var object = new Object();
+ a[0] = 0;
+ var count = 3;
+ do {
+ if (a.baz != true) {
+ a[1] = 2.5;
+ }
+ a[0] = object;
+ } while (--count > 3);
+ }
+
+ testDominatingTransitionHoisting1(new Array(5));
+ %OptimizeFunctionOnNextCall(testDominatingTransitionHoisting1);
+ testDominatingTransitionHoisting1(new Array(5));
+ testDominatingTransitionHoisting1(new Array(5));
+
assertTrue(2 != %GetOptimizationStatus(testDominatingTransitionHoisting1));
+
+ function testHoistingWithSideEffect(a) {
+ var object = new Object();
+ a[0] = 0;
+ var count = 3;
+ do {
+ assertTrue(true);
+ a[0] = object;
+ } while (--count > 3);
+ }
+
+ testHoistingWithSideEffect(new Array(5));
+ %OptimizeFunctionOnNextCall(testHoistingWithSideEffect);
+ testHoistingWithSideEffect(new Array(5));
+ testHoistingWithSideEffect(new Array(5));
+ assertTrue(2 != %GetOptimizationStatus(testHoistingWithSideEffect));
+}
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Wed Feb 1
00:49:18 2012
+++ /branches/bleeding_edge/src/hydrogen-instructions.cc Thu Feb 9
00:58:19 2012
@@ -893,6 +893,13 @@
void HCheckMap::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add(" %p", *map());
+ if (mode() == REQUIRE_EXACT_MAP) {
+ stream->Add(" [EXACT]");
+ } else if (!has_element_transitions_) {
+ stream->Add(" [EXACT*]");
+ } else {
+ stream->Add(" [MATCH ELEMENTS]");
+ }
}
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Wed Feb 1 00:49:18
2012
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Thu Feb 9 00:58:19
2012
@@ -186,6 +186,7 @@
V(InobjectFields) \
V(BackingStoreFields) \
V(ElementsKind) \
+ V(ElementsPointer) \
V(ArrayElements) \
V(DoubleArrayElements) \
V(SpecializedArrayElements) \
@@ -645,6 +646,18 @@
bool HasObservableSideEffects() const {
return gvn_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet());
}
+
+ GVNFlagSet DependsOnFlags() const {
+ GVNFlagSet result = gvn_flags_;
+ result.Intersect(AllDependsOnFlagSet());
+ return result;
+ }
+
+ GVNFlagSet SideEffectFlags() const {
+ GVNFlagSet result = gvn_flags_;
+ result.Intersect(AllSideEffectsFlagSet());
+ return result;
+ }
GVNFlagSet ChangesFlags() const {
GVNFlagSet result = gvn_flags_;
@@ -721,6 +734,15 @@
ASSERT(representation_.IsNone() && !r.IsNone());
representation_ = r;
}
+
+ static GVNFlagSet AllDependsOnFlagSet() {
+ GVNFlagSet result;
+ // Create changes mask.
+#define ADD_FLAG(type) result.Add(kDependsOn##type);
+ GVN_FLAG_LIST(ADD_FLAG)
+#undef ADD_FLAG
+ return result;
+ }
static GVNFlagSet AllChangesFlagSet() {
GVNFlagSet result;
@@ -743,6 +765,8 @@
static GVNFlagSet AllObservableSideEffectsFlagSet() {
GVNFlagSet result = AllChangesFlagSet();
result.Remove(kChangesElementsKind);
+ result.Remove(kChangesElementsPointer);
+ result.Remove(kChangesMaps);
return result;
}
@@ -1920,8 +1944,7 @@
explicit HLoadElements(HValue* value) : HUnaryOperation(value) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
- SetGVNFlag(kDependsOnMaps);
- SetGVNFlag(kDependsOnElementsKind);
+ SetGVNFlag(kDependsOnElementsPointer);
}
virtual Representation RequiredInputRepresentation(int index) {
@@ -1972,6 +1995,11 @@
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
SetGVNFlag(kDependsOnMaps);
+ // If the map to check doesn't have the untransitioned elements, it
must not
+ // be hoisted above TransitionElements instructions.
+ if (mode == REQUIRE_EXACT_MAP || !map->has_fast_smi_only_elements()) {
+ SetGVNFlag(kDependsOnElementsKind);
+ }
has_element_transitions_ =
map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL) !=
NULL ||
map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL) != NULL;
@@ -4135,7 +4163,17 @@
transitioned_map_(transitioned_map) {
SetOperandAt(0, object);
SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnMaps);
SetGVNFlag(kChangesElementsKind);
+ if (original_map->has_fast_double_elements()) {
+ SetGVNFlag(kChangesElementsPointer);
+ SetGVNFlag(kDependsOnElementsPointer);
+ SetGVNFlag(kDependsOnDoubleArrayElements);
+ } else if (transitioned_map->has_fast_double_elements()) {
+ SetGVNFlag(kChangesElementsPointer);
+ SetGVNFlag(kDependsOnElementsPointer);
+ SetGVNFlag(kDependsOnArrayElements);
+ }
set_representation(Representation::Tagged());
}
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Wed Feb 8 07:39:41 2012
+++ /branches/bleeding_edge/src/hydrogen.cc Thu Feb 9 00:58:19 2012
@@ -70,7 +70,8 @@
deleted_phis_(4),
parent_loop_header_(NULL),
is_inline_return_target_(false),
- is_deoptimizing_(false) { }
+ is_deoptimizing_(false),
+ dominates_loop_successors_(false) { }
void HBasicBlock::AttachLoopInformation() {
@@ -313,6 +314,62 @@
}
}
}
+
+
+void HBasicBlock::AssignLoopSuccessorDominators() {
+ // Mark blocks that dominate all subsequent reachable blocks inside their
+ // loop. Exploit the fact that blocks are sorted in reverse post order.
When
+ // the loop is visited in increasing block id order, if the number of
+ // non-loop-exiting successor edges at the dominator_candidate block
doesn't
+ // exceed the number of previously encountered predecessor edges, there
is no
+ // path from the loop header to any block with higher id that doesn't go
+ // through the dominator_candidate block. In this case, the
+ // dominator_candidate block is guaranteed to dominate all blocks
reachable
+ // from it with higher ids.
+ HBasicBlock* last = loop_information()->GetLastBackEdge();
+ int outstanding_successors = 1; // one edge from the pre-header
+ // Header always dominates everything.
+ MarkAsLoopSuccessorDominator();
+ for (int j = block_id(); j <= last->block_id(); ++j) {
+ HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
+ for (HPredecessorIterator it(dominator_candidate); !it.Done();
+ it.Advance()) {
+ HBasicBlock* predecessor = it.Current();
+ // Don't count back edges.
+ if (predecessor->block_id() < dominator_candidate->block_id()) {
+ outstanding_successors--;
+ }
+ }
+
+ // If more successors than predecessors have been seen in the loop up
to
+ // now, it's not possible to guarantee that the current block dominates
+ // all of the blocks with higher IDs. In this case, assume
conservatively
+ // that those paths through loop that don't go through the current
block
+ // contain all of the loop's dependencies. Also be careful to record
+ // dominator information about the current loop that's being processed,
+ // and not nested loops, which will be processed when
+ // AssignLoopSuccessorDominators gets called on their header.
+ ASSERT(outstanding_successors >= 0);
+ HBasicBlock* parent_loop_header =
dominator_candidate->parent_loop_header();
+ if (outstanding_successors == 0 &&
+ (parent_loop_header == this
&& !dominator_candidate->IsLoopHeader())) {
+ dominator_candidate->MarkAsLoopSuccessorDominator();
+ }
+ HControlInstruction* end = dominator_candidate->end();
+ for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
+ HBasicBlock* successor = it.Current();
+ // Only count successors that remain inside the loop and don't loop
back
+ // to a loop header.
+ if (successor->block_id() > dominator_candidate->block_id() &&
+ successor->block_id() <= last->block_id()) {
+ // Backwards edges must land on loop headers.
+ ASSERT(successor->block_id() > dominator_candidate->block_id() ||
+ successor->IsLoopHeader());
+ outstanding_successors++;
+ }
+ }
+ }
+}
int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
@@ -750,10 +807,12 @@
void HGraph::AssignDominators() {
HPhase phase("Assign dominators", this);
for (int i = 0; i < blocks_.length(); ++i) {
- if (blocks_[i]->IsLoopHeader()) {
+ HBasicBlock* block = blocks_[i];
+ if (block->IsLoopHeader()) {
// Only the first predecessor of a loop header is from outside the
loop.
// All others are back edges, and thus cannot dominate the loop
header.
-
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
+ block->AssignCommonDominator(block->predecessors()->first());
+ block->AssignLoopSuccessorDominators();
} else {
for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
@@ -1371,7 +1430,8 @@
void LoopInvariantCodeMotion();
void ProcessLoopBlock(HBasicBlock* block,
HBasicBlock* before_loop,
- GVNFlagSet loop_kills);
+ GVNFlagSet loop_kills,
+ GVNFlagSet* accumulated_first_time_depends);
bool AllowCodeMotion();
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
@@ -1396,6 +1456,7 @@
bool HGlobalValueNumberer::Analyze() {
+ removed_side_effects_ = false;
ComputeBlockSideEffects();
if (FLAG_loop_invariant_code_motion) {
LoopInvariantCodeMotion();
@@ -1407,6 +1468,12 @@
void HGlobalValueNumberer::ComputeBlockSideEffects() {
+ // The Analyze phase of GVN can be called multiple times. Clear loop side
+ // effects before computing them to erase the contents from previous
Analyze
+ // passes.
+ for (int i = 0; i < loop_side_effects_.length(); ++i) {
+ loop_side_effects_[i].RemoveAll();
+ }
for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
// Compute side effects for the block.
HBasicBlock* block = graph_->blocks()->at(i);
@@ -1444,18 +1511,22 @@
block->block_id(),
side_effects.ToIntegral());
+ GVNFlagSet accumulated_first_time_depends;
HBasicBlock* last = block->loop_information()->GetLastBackEdge();
for (int j = block->block_id(); j <= last->block_id(); ++j) {
- ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
+ ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
+ &accumulated_first_time_depends);
}
}
}
}
-void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
- HBasicBlock* loop_header,
- GVNFlagSet loop_kills) {
+void HGlobalValueNumberer::ProcessLoopBlock(
+ HBasicBlock* block,
+ HBasicBlock* loop_header,
+ GVNFlagSet loop_kills,
+ GVNFlagSet* accumulated_first_time_depends) {
HBasicBlock* pre_header = loop_header->predecessors()->at(0);
GVNFlagSet depends_flags =
HValue::ConvertChangesToDependsFlags(loop_kills);
TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
@@ -1464,25 +1535,65 @@
HInstruction* instr = block->first();
while (instr != NULL) {
HInstruction* next = instr->next();
- if (instr->CheckFlag(HValue::kUseGVN) &&
- !instr->gvn_flags().ContainsAnyOf(depends_flags)) {
- TraceGVN("Checking instruction %d (%s)\n",
+ bool hoisted = false;
+ if (instr->CheckFlag(HValue::kUseGVN)) {
+ TraceGVN("Checking instruction %d (%s) instruction GVN flags 0x%X, "
+ "loop kills 0x%X\n",
instr->id(),
- instr->Mnemonic());
- bool inputs_loop_invariant = true;
- for (int i = 0; i < instr->OperandCount(); ++i) {
- if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
- inputs_loop_invariant = false;
- }
+ instr->Mnemonic(),
+ instr->gvn_flags().ToIntegral(),
+ depends_flags.ToIntegral());
+ bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
+ if (!can_hoist && instr->IsTransitionElementsKind()) {
+ // It's only possible to hoist one time side effects if there are
no
+ // dependencies on their changes from the loop header to the
current
+ // instruction.
+ GVNFlagSet converted_changes =
+ HValue::ConvertChangesToDependsFlags(instr->ChangesFlags());
+ TraceGVN("Checking dependencies on one-time instruction %d (%s) "
+ "converted changes 0x%X, accumulated depends 0x%X\n",
+ instr->id(),
+ instr->Mnemonic(),
+ converted_changes.ToIntegral(),
+ accumulated_first_time_depends->ToIntegral());
+ // It's possible to hoist one-time side effects from the current
loop
+ // loop only if they dominate all of the successor blocks in the
same
+ // loop and there are not any instructions that have
Changes/DependsOn
+ // that intervene between it and the beginning of the loop header.
+ bool in_nested_loop = block != loop_header &&
+ ((block->parent_loop_header() != loop_header) ||
+ block->IsLoopHeader());
+ can_hoist = !in_nested_loop &&
+ block->IsLoopSuccessorDominator() &&
+ !accumulated_first_time_depends->ContainsAnyOf(converted_changes);
}
- if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
- TraceGVN("Found loop invariant instruction %d\n", instr->id());
- // Move the instruction out of the loop.
- instr->Unlink();
- instr->InsertBefore(pre_header->end());
+ if (can_hoist) {
+ bool inputs_loop_invariant = true;
+ for (int i = 0; i < instr->OperandCount(); ++i) {
+ if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
+ inputs_loop_invariant = false;
+ }
+ }
+
+ if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
+ TraceGVN("Hoisting loop invariant instruction %d\n",
instr->id());
+ // Move the instruction out of the loop.
+ instr->Unlink();
+ instr->InsertBefore(pre_header->end());
+ if (instr->HasSideEffects()) removed_side_effects_ = true;
+ hoisted = true;
+ }
}
}
+ if (!hoisted) {
+ // If an instruction is not hoisted, we have to account for its side
+ // effects when hoisting later HTransitionElementsKind instructions.
+ accumulated_first_time_depends->Add(instr->DependsOnFlags());
+ GVNFlagSet converted_changes =
+ HValue::ConvertChangesToDependsFlags(instr->SideEffectFlags());
+ accumulated_first_time_depends->Add(converted_changes);
+ }
instr = next;
}
}
@@ -2390,7 +2501,8 @@
// could only be discovered by removing side-effect-generating
instructions
// during the first pass.
if (FLAG_smi_only_arrays && removed_side_effects) {
- gvn.Analyze();
+ removed_side_effects = gvn.Analyze();
+ ASSERT(!removed_side_effects);
}
}
@@ -7260,7 +7372,10 @@
}
PrintEmptyProperty("xhandlers");
- PrintEmptyProperty("flags");
+ const char* flags = current->IsLoopSuccessorDominator()
+ ? "dom-loop-succ"
+ : "";
+ PrintStringProperty("flags", flags);
if (current->dominator() != NULL) {
PrintBlockProperty("dominator", current->dominator()->block_id());
=======================================
--- /branches/bleeding_edge/src/hydrogen.h Wed Feb 8 04:08:46 2012
+++ /branches/bleeding_edge/src/hydrogen.h Thu Feb 9 00:58:19 2012
@@ -126,6 +126,7 @@
int PredecessorIndexOf(HBasicBlock* predecessor) const;
void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); }
void AssignCommonDominator(HBasicBlock* other);
+ void AssignLoopSuccessorDominators();
void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) {
FinishExit(CreateDeoptimize(has_uses));
@@ -148,6 +149,13 @@
bool IsDeoptimizing() const { return is_deoptimizing_; }
void MarkAsDeoptimizing() { is_deoptimizing_ = true; }
+
+ bool IsLoopSuccessorDominator() const {
+ return dominates_loop_successors_;
+ }
+ void MarkAsLoopSuccessorDominator() {
+ dominates_loop_successors_ = true;
+ }
inline Zone* zone();
@@ -182,6 +190,22 @@
HBasicBlock* parent_loop_header_;
bool is_inline_return_target_;
bool is_deoptimizing_;
+ bool dominates_loop_successors_;
+};
+
+
+class HPredecessorIterator BASE_EMBEDDED {
+ public:
+ explicit HPredecessorIterator(HBasicBlock* block)
+ : predecessor_list_(block->predecessors()), current_(0) { }
+
+ bool Done() { return current_ >= predecessor_list_->length(); }
+ HBasicBlock* Current() { return predecessor_list_->at(current_); }
+ void Advance() { current_++; }
+
+ private:
+ const ZoneList<HBasicBlock*>* predecessor_list_;
+ int current_;
};
=======================================
--- /branches/bleeding_edge/src/objects.cc Tue Feb 7 23:23:14 2012
+++ /branches/bleeding_edge/src/objects.cc Thu Feb 9 00:58:19 2012
@@ -5511,7 +5511,7 @@
for (int i = 0; i < maps_->length(); ++i) {
bool match_found = false;
for (int j = 0; j < other_maps.length(); ++j) {
- if (maps_->at(i)->EquivalentTo(*other_maps.at(j))) {
+ if (*(maps_->at(i)) == *(other_maps.at(j))) {
match_found = true;
break;
}
=======================================
--- /branches/bleeding_edge/src/objects.h Wed Feb 8 01:56:33 2012
+++ /branches/bleeding_edge/src/objects.h Thu Feb 9 00:58:19 2012
@@ -4695,12 +4695,6 @@
// it had exactly zero inobject properties.
// The "shared" flags of both this map and |other| are ignored.
bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode
mode);
-
- // Returns true if this map and |other| describe equivalent objects.
- // The "shared" flags of both this map and |other| are ignored.
- bool EquivalentTo(Map* other) {
- return EquivalentToForNormalization(other, KEEP_INOBJECT_PROPERTIES);
- }
// Returns the contents of this map's descriptor array for the given
string.
// May return NULL. |safe_to_add_transition| is set to false and NULL
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev