Commit 9559181b authored by yangguo@chromium.org's avatar yangguo@chromium.org

Fix worst-case behavior of MergeRemovableSimulates().

Currently, when a long series of removable simulates are merged, we do
this by merging them one by one as we find them.  As we merge the value
value lists of the simulates, those lists snowball so that we get a
quadratic complexity wrt runtime and memory consumption.

Instead, we gather simulates that need to be merged, and merge them
backwards starting from the last simulate.

R=jkummerow@chromium.org
BUG=v8:2612

Review URL: https://chromiumcodereview.appspot.com/13649003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14169 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 3b65eccc
...@@ -685,7 +685,7 @@ void HValue::Kill() { ...@@ -685,7 +685,7 @@ void HValue::Kill() {
HValue* operand = OperandAt(i); HValue* operand = OperandAt(i);
if (operand == NULL) continue; if (operand == NULL) continue;
HUseListNode* first = operand->use_list_; HUseListNode* first = operand->use_list_;
if (first != NULL && first->value() == this && first->index() == i) { if (first != NULL && first->value()->CheckFlag(kIsDead)) {
operand->use_list_ = first->tail(); operand->use_list_ = first->tail();
} }
} }
...@@ -1980,20 +1980,25 @@ void HPhi::AddIndirectUsesTo(int* dest) { ...@@ -1980,20 +1980,25 @@ void HPhi::AddIndirectUsesTo(int* dest) {
} }
void HSimulate::MergeInto(HSimulate* other) { void HSimulate::MergeWith(ZoneList<HSimulate*>* list) {
for (int i = 0; i < values_.length(); ++i) { while (!list->is_empty()) {
HValue* value = values_[i]; HSimulate* from = list->RemoveLast();
if (HasAssignedIndexAt(i)) { ZoneList<HValue*>* from_values = &from->values_;
other->AddAssignedValue(GetAssignedIndexAt(i), value); for (int i = 0; i < from_values->length(); ++i) {
} else { if (from->HasAssignedIndexAt(i)) {
if (other->pop_count_ > 0) { AddAssignedValue(from->GetAssignedIndexAt(i),
other->pop_count_--; from_values->at(i));
} else { } else {
other->AddPushedValue(value); if (pop_count_ > 0) {
pop_count_--;
} else {
AddPushedValue(from_values->at(i));
}
} }
} }
pop_count_ += from->pop_count_;
from->DeleteAndReplaceWith(NULL);
} }
other->pop_count_ += pop_count();
} }
......
...@@ -1830,7 +1830,7 @@ class HSimulate: public HInstruction { ...@@ -1830,7 +1830,7 @@ class HSimulate: public HInstruction {
return Representation::None(); return Representation::None();
} }
void MergeInto(HSimulate* other); void MergeWith(ZoneList<HSimulate*>* list);
bool is_candidate_for_removal() { return removable_ == REMOVABLE_SIMULATE; } bool is_candidate_for_removal() { return removable_ == REMOVABLE_SIMULATE; }
DECLARE_CONCRETE_INSTRUCTION(Simulate) DECLARE_CONCRETE_INSTRUCTION(Simulate)
......
...@@ -3427,10 +3427,11 @@ void HInferRepresentation::Analyze() { ...@@ -3427,10 +3427,11 @@ void HInferRepresentation::Analyze() {
void HGraph::MergeRemovableSimulates() { void HGraph::MergeRemovableSimulates() {
ZoneList<HSimulate*> mergelist(2, zone());
for (int i = 0; i < blocks()->length(); ++i) { for (int i = 0; i < blocks()->length(); ++i) {
HBasicBlock* block = blocks()->at(i); HBasicBlock* block = blocks()->at(i);
// Always reset the folding candidate at the start of a block. // Make sure the merge list is empty at the start of a block.
HSimulate* folding_candidate = NULL; ASSERT(mergelist.is_empty());
// Nasty heuristic: Never remove the first simulate in a block. This // Nasty heuristic: Never remove the first simulate in a block. This
// just so happens to have a beneficial effect on register allocation. // just so happens to have a beneficial effect on register allocation.
bool first = true; bool first = true;
...@@ -3441,33 +3442,38 @@ void HGraph::MergeRemovableSimulates() { ...@@ -3441,33 +3442,38 @@ void HGraph::MergeRemovableSimulates() {
// in the outer environment. // in the outer environment.
// (Before each HEnterInlined, there is a non-foldable HSimulate // (Before each HEnterInlined, there is a non-foldable HSimulate
// anyway, so we get the barrier in the other direction for free.) // anyway, so we get the barrier in the other direction for free.)
if (folding_candidate != NULL) { // Simply remove all accumulated simulates without merging. This
folding_candidate->DeleteAndReplaceWith(NULL); // is safe because simulates after instructions with side effects
// are never added to the merge list.
while (!mergelist.is_empty()) {
mergelist.RemoveLast()->DeleteAndReplaceWith(NULL);
} }
folding_candidate = NULL;
continue; continue;
} }
// If we have an HSimulate and a candidate, perform the folding. // Skip the non-simulates and the first simulate.
if (!current->IsSimulate()) continue; if (!current->IsSimulate()) continue;
if (first) { if (first) {
first = false; first = false;
continue; continue;
} }
HSimulate* current_simulate = HSimulate::cast(current); HSimulate* current_simulate = HSimulate::cast(current);
if (folding_candidate != NULL) { if ((current_simulate->previous()->HasObservableSideEffects() &&
folding_candidate->MergeInto(current_simulate); !current_simulate->next()->IsSimulate()) ||
folding_candidate->DeleteAndReplaceWith(NULL); !current_simulate->is_candidate_for_removal()) {
folding_candidate = NULL; // This simulate is not suitable for folding.
} // Fold the ones accumulated so far.
// Check if the current simulate is a candidate for folding. current_simulate->MergeWith(&mergelist);
if (current_simulate->previous()->HasObservableSideEffects() &&
!current_simulate->next()->IsSimulate()) {
continue;
}
if (!current_simulate->is_candidate_for_removal()) {
continue; continue;
} else {
// Accumulate this simulate for folding later on.
mergelist.Add(current_simulate, zone());
} }
folding_candidate = current_simulate; }
if (!mergelist.is_empty()) {
// Merge the accumulated simulates at the end of the block.
HSimulate* last = mergelist.RemoveLast();
last->MergeWith(&mergelist);
} }
} }
} }
......
...@@ -49,6 +49,7 @@ compiler/regress-stacktrace-methods: PASS, SKIP if $mode == debug ...@@ -49,6 +49,7 @@ compiler/regress-stacktrace-methods: PASS, SKIP if $mode == debug
compiler/regress-funcaller: PASS, SKIP if $mode == debug compiler/regress-funcaller: PASS, SKIP if $mode == debug
regress/regress-2318: PASS, SKIP if $mode == debug regress/regress-2318: PASS, SKIP if $mode == debug
regress/regress-create-exception: PASS, SKIP if $mode == debug regress/regress-create-exception: PASS, SKIP if $mode == debug
regress/regress-2612: PASS, SKIP if $mode == debug
############################################################################## ##############################################################################
# Too slow in debug mode for GC stress mode. # Too slow in debug mode for GC stress mode.
......
// Copyright 2013 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 --nodead-code-elimination
// Flags: --nofold-constants --nouse-gvn
// Create a function to get a long series of removable simulates.
// f() {
// var _0 = <random>, _1 = <random>, ... _1000 = <random>,
// _1001 = <random var> + <random var>,
// _1002 = <random var> + <random var>,
// ...
// _99999 = <random var> + <random var>,
// x = 1;
// return _0;
// }
var seed = 1;
function rand() {
seed = seed * 171 % 1337 + 17;
return (seed % 1000) / 1000;
}
function randi(max) {
seed = seed * 131 % 1773 + 13;
return seed % max;
}
function varname(i) {
return "_" + i;
}
var source = "var ";
for (var i = 0; i < 1000; i++) {
source += [varname(i), "=", rand(), ","].join("");
}
for (var i = 1000; i < 100000; i++) {
source += [varname(i), "=",
varname(randi(i)), "+",
varname(randi(i)), ","].join("");
}
source += "x=1; return _0;"
var f = new Function(source);
f();
%OptimizeFunctionOnNextCall(f);
f();
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