Enable optimization of functions with generic switches.
R=jkummerow@chromium.org, titzer@chromium.org Review URL: https://chromiumcodereview.appspot.com/110123002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18347 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
b01b6dc2cf
commit
fb7218dc3d
@ -1025,6 +1025,16 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
||||||
patch_site.EmitPatchInfo();
|
patch_site.EmitPatchInfo();
|
||||||
|
|
||||||
|
Label skip;
|
||||||
|
__ b(&skip);
|
||||||
|
PrepareForBailout(clause, TOS_REG);
|
||||||
|
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
|
||||||
|
__ cmp(r0, ip);
|
||||||
|
__ b(ne, &next_test);
|
||||||
|
__ Drop(1);
|
||||||
|
__ jmp(clause->body_target());
|
||||||
|
__ bind(&skip);
|
||||||
|
|
||||||
__ cmp(r0, Operand::Zero());
|
__ cmp(r0, Operand::Zero());
|
||||||
__ b(ne, &next_test);
|
__ b(ne, &next_test);
|
||||||
__ Drop(1); // Switch value is no longer needed.
|
__ Drop(1); // Switch value is no longer needed.
|
||||||
|
@ -1121,7 +1121,7 @@ CaseClause::CaseClause(Isolate* isolate,
|
|||||||
Expression* label,
|
Expression* label,
|
||||||
ZoneList<Statement*>* statements,
|
ZoneList<Statement*>* statements,
|
||||||
int pos)
|
int pos)
|
||||||
: AstNode(pos),
|
: Expression(isolate, pos),
|
||||||
label_(label),
|
label_(label),
|
||||||
statements_(statements),
|
statements_(statements),
|
||||||
compare_type_(Type::None(), isolate),
|
compare_type_(Type::None(), isolate),
|
||||||
|
@ -115,17 +115,14 @@ namespace internal {
|
|||||||
V(CountOperation) \
|
V(CountOperation) \
|
||||||
V(BinaryOperation) \
|
V(BinaryOperation) \
|
||||||
V(CompareOperation) \
|
V(CompareOperation) \
|
||||||
V(ThisFunction)
|
V(ThisFunction) \
|
||||||
|
|
||||||
#define AUXILIARY_NODE_LIST(V) \
|
|
||||||
V(CaseClause)
|
V(CaseClause)
|
||||||
|
|
||||||
#define AST_NODE_LIST(V) \
|
#define AST_NODE_LIST(V) \
|
||||||
DECLARATION_NODE_LIST(V) \
|
DECLARATION_NODE_LIST(V) \
|
||||||
MODULE_NODE_LIST(V) \
|
MODULE_NODE_LIST(V) \
|
||||||
STATEMENT_NODE_LIST(V) \
|
STATEMENT_NODE_LIST(V) \
|
||||||
EXPRESSION_NODE_LIST(V) \
|
EXPRESSION_NODE_LIST(V)
|
||||||
AUXILIARY_NODE_LIST(V)
|
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class AstConstructionVisitor;
|
class AstConstructionVisitor;
|
||||||
@ -1105,7 +1102,7 @@ class WithStatement V8_FINAL : public Statement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class CaseClause V8_FINAL : public AstNode {
|
class CaseClause V8_FINAL : public Expression {
|
||||||
public:
|
public:
|
||||||
DECLARE_NODE_TYPE(CaseClause)
|
DECLARE_NODE_TYPE(CaseClause)
|
||||||
|
|
||||||
|
@ -355,7 +355,6 @@ class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder {
|
|||||||
}
|
}
|
||||||
MODULE_NODE_LIST(DEF_VISIT)
|
MODULE_NODE_LIST(DEF_VISIT)
|
||||||
DECLARATION_NODE_LIST(DEF_VISIT)
|
DECLARATION_NODE_LIST(DEF_VISIT)
|
||||||
AUXILIARY_NODE_LIST(DEF_VISIT)
|
|
||||||
#undef DEF_VISIT
|
#undef DEF_VISIT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4141,31 +4141,31 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
const int kCaseClauseLimit = 128;
|
const int kCaseClauseLimit = 128;
|
||||||
ZoneList<CaseClause*>* clauses = stmt->cases();
|
ZoneList<CaseClause*>* clauses = stmt->cases();
|
||||||
int clause_count = clauses->length();
|
int clause_count = clauses->length();
|
||||||
|
ZoneList<HBasicBlock*> body_blocks(clause_count, zone());
|
||||||
if (clause_count > kCaseClauseLimit) {
|
if (clause_count > kCaseClauseLimit) {
|
||||||
return Bailout(kSwitchStatementTooManyClauses);
|
return Bailout(kSwitchStatementTooManyClauses);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
|
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
|
||||||
if (stmt->switch_type() == SwitchStatement::GENERIC_SWITCH) {
|
|
||||||
return Bailout(kSwitchStatementMixedOrNonLiteralSwitchLabels);
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_ALIVE(VisitForValue(stmt->tag()));
|
CHECK_ALIVE(VisitForValue(stmt->tag()));
|
||||||
Add<HSimulate>(stmt->EntryId());
|
Add<HSimulate>(stmt->EntryId());
|
||||||
HValue* tag_value = Pop();
|
HValue* tag_value = Top();
|
||||||
HBasicBlock* first_test_block = current_block();
|
|
||||||
|
|
||||||
HUnaryControlInstruction* string_check = NULL;
|
HUnaryControlInstruction* string_check = NULL;
|
||||||
HBasicBlock* not_string_block = NULL;
|
HBasicBlock* not_string_block = NULL;
|
||||||
|
|
||||||
// Test switch's tag value if all clauses are string literals
|
// Test switch's tag value if all clauses are string literals
|
||||||
if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
|
if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
|
||||||
first_test_block = graph()->CreateBasicBlock();
|
HBasicBlock* first_test_block = graph()->CreateBasicBlock();
|
||||||
not_string_block = graph()->CreateBasicBlock();
|
not_string_block = graph()->CreateBasicBlock();
|
||||||
string_check = New<HIsStringAndBranch>(
|
string_check = New<HIsStringAndBranch>(
|
||||||
tag_value, first_test_block, not_string_block);
|
tag_value, first_test_block, not_string_block);
|
||||||
FinishCurrentBlock(string_check);
|
FinishCurrentBlock(string_check);
|
||||||
|
|
||||||
|
set_current_block(not_string_block);
|
||||||
|
Drop(1); // tag_value
|
||||||
|
|
||||||
set_current_block(first_test_block);
|
set_current_block(first_test_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4174,7 +4174,8 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
for (int i = 0; i < clause_count; ++i) {
|
for (int i = 0; i < clause_count; ++i) {
|
||||||
CaseClause* clause = clauses->at(i);
|
CaseClause* clause = clauses->at(i);
|
||||||
if (clause->is_default()) {
|
if (clause->is_default()) {
|
||||||
default_id = clause->EntryId();
|
body_blocks.Add(NULL, zone());
|
||||||
|
if (default_id.IsNone()) default_id = clause->EntryId();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4182,9 +4183,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
CHECK_ALIVE(VisitForValue(clause->label()));
|
CHECK_ALIVE(VisitForValue(clause->label()));
|
||||||
HValue* label_value = Pop();
|
HValue* label_value = Pop();
|
||||||
|
|
||||||
HBasicBlock* next_test_block = graph()->CreateBasicBlock();
|
|
||||||
HBasicBlock* body_block = graph()->CreateBasicBlock();
|
|
||||||
|
|
||||||
HControlInstruction* compare;
|
HControlInstruction* compare;
|
||||||
|
|
||||||
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
|
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
|
||||||
@ -4199,22 +4197,39 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
compare_->set_observed_input_representation(
|
compare_->set_observed_input_representation(
|
||||||
Representation::Smi(), Representation::Smi());
|
Representation::Smi(), Representation::Smi());
|
||||||
compare = compare_;
|
compare = compare_;
|
||||||
} else {
|
} else if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
|
||||||
compare = New<HStringCompareAndBranch>(tag_value,
|
compare = New<HStringCompareAndBranch>(tag_value,
|
||||||
label_value,
|
label_value,
|
||||||
Token::EQ_STRICT);
|
Token::EQ_STRICT);
|
||||||
|
} else {
|
||||||
|
HValue* test = Add<HCompareGeneric>(tag_value,
|
||||||
|
label_value,
|
||||||
|
Token::EQ_STRICT);
|
||||||
|
if (test->HasObservableSideEffects()) {
|
||||||
|
Push(test);
|
||||||
|
Add<HSimulate>(clause->id(), REMOVABLE_SIMULATE);
|
||||||
|
Drop(1);
|
||||||
|
}
|
||||||
|
compare = New<HBranch>(test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HBasicBlock* next_test_block = graph()->CreateBasicBlock();
|
||||||
|
HBasicBlock* body_block = graph()->CreateBasicBlock();
|
||||||
|
body_blocks.Add(body_block, zone());
|
||||||
compare->SetSuccessorAt(0, body_block);
|
compare->SetSuccessorAt(0, body_block);
|
||||||
compare->SetSuccessorAt(1, next_test_block);
|
compare->SetSuccessorAt(1, next_test_block);
|
||||||
FinishCurrentBlock(compare);
|
FinishCurrentBlock(compare);
|
||||||
|
|
||||||
|
set_current_block(body_block);
|
||||||
|
Drop(1); // tag_value
|
||||||
|
|
||||||
set_current_block(next_test_block);
|
set_current_block(next_test_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the current block to use for the default or to join with the
|
// Save the current block to use for the default or to join with the
|
||||||
// exit.
|
// exit.
|
||||||
HBasicBlock* last_block = current_block();
|
HBasicBlock* last_block = current_block();
|
||||||
|
Drop(1); // tag_value
|
||||||
|
|
||||||
if (not_string_block != NULL) {
|
if (not_string_block != NULL) {
|
||||||
BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
|
BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
|
||||||
@ -4223,7 +4238,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
|
|
||||||
// 2. Loop over the clauses and the linked list of tests in lockstep,
|
// 2. Loop over the clauses and the linked list of tests in lockstep,
|
||||||
// translating the clause bodies.
|
// translating the clause bodies.
|
||||||
HBasicBlock* curr_test_block = first_test_block;
|
|
||||||
HBasicBlock* fall_through_block = NULL;
|
HBasicBlock* fall_through_block = NULL;
|
||||||
|
|
||||||
BreakAndContinueInfo break_info(stmt);
|
BreakAndContinueInfo break_info(stmt);
|
||||||
@ -4235,40 +4249,16 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
// goes to.
|
// goes to.
|
||||||
HBasicBlock* normal_block = NULL;
|
HBasicBlock* normal_block = NULL;
|
||||||
if (clause->is_default()) {
|
if (clause->is_default()) {
|
||||||
if (last_block != NULL) {
|
if (last_block == NULL) continue;
|
||||||
normal_block = last_block;
|
normal_block = last_block;
|
||||||
last_block = NULL; // Cleared to indicate we've handled it.
|
last_block = NULL; // Cleared to indicate we've handled it.
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// If the current test block is deoptimizing due to an unhandled clause
|
normal_block = body_blocks[i];
|
||||||
// of the switch, the test instruction is in the next block since the
|
|
||||||
// deopt must end the current block.
|
|
||||||
if (curr_test_block->IsDeoptimizing()) {
|
|
||||||
ASSERT(curr_test_block->end()->SecondSuccessor() == NULL);
|
|
||||||
curr_test_block = curr_test_block->end()->FirstSuccessor();
|
|
||||||
}
|
|
||||||
normal_block = curr_test_block->end()->FirstSuccessor();
|
|
||||||
curr_test_block = curr_test_block->end()->SecondSuccessor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify a block to emit the body into.
|
if (fall_through_block == NULL) {
|
||||||
if (normal_block == NULL) {
|
|
||||||
if (fall_through_block == NULL) {
|
|
||||||
// (a) Unreachable.
|
|
||||||
if (clause->is_default()) {
|
|
||||||
continue; // Might still be reachable clause bodies.
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// (b) Reachable only as fall through.
|
|
||||||
set_current_block(fall_through_block);
|
|
||||||
}
|
|
||||||
} else if (fall_through_block == NULL) {
|
|
||||||
// (c) Reachable only normally.
|
|
||||||
set_current_block(normal_block);
|
set_current_block(normal_block);
|
||||||
} else {
|
} else {
|
||||||
// (d) Reachable both ways.
|
|
||||||
HBasicBlock* join = CreateJoin(fall_through_block,
|
HBasicBlock* join = CreateJoin(fall_through_block,
|
||||||
normal_block,
|
normal_block,
|
||||||
clause->EntryId());
|
clause->EntryId());
|
||||||
|
@ -977,6 +977,16 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
|
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
|
||||||
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
||||||
patch_site.EmitPatchInfo();
|
patch_site.EmitPatchInfo();
|
||||||
|
|
||||||
|
Label skip;
|
||||||
|
__ jmp(&skip, Label::kNear);
|
||||||
|
PrepareForBailout(clause, TOS_REG);
|
||||||
|
__ cmp(eax, isolate()->factory()->true_value());
|
||||||
|
__ j(not_equal, &next_test);
|
||||||
|
__ Drop(1);
|
||||||
|
__ jmp(clause->body_target());
|
||||||
|
__ bind(&skip);
|
||||||
|
|
||||||
__ test(eax, eax);
|
__ test(eax, eax);
|
||||||
__ j(not_equal, &next_test);
|
__ j(not_equal, &next_test);
|
||||||
__ Drop(1); // Switch value is no longer needed.
|
__ Drop(1); // Switch value is no longer needed.
|
||||||
|
@ -207,11 +207,6 @@ void Processor::VisitSwitchStatement(SwitchStatement* node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Processor::VisitCaseClause(CaseClause* clause) {
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Processor::VisitContinueStatement(ContinueStatement* node) {
|
void Processor::VisitContinueStatement(ContinueStatement* node) {
|
||||||
is_set_ = false;
|
is_set_ = false;
|
||||||
}
|
}
|
||||||
|
@ -986,6 +986,15 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
|
|||||||
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
|
||||||
patch_site.EmitPatchInfo();
|
patch_site.EmitPatchInfo();
|
||||||
|
|
||||||
|
Label skip;
|
||||||
|
__ jmp(&skip, Label::kNear);
|
||||||
|
PrepareForBailout(clause, TOS_REG);
|
||||||
|
__ CompareRoot(rax, Heap::kTrueValueRootIndex);
|
||||||
|
__ j(not_equal, &next_test);
|
||||||
|
__ Drop(1);
|
||||||
|
__ jmp(clause->body_target());
|
||||||
|
__ bind(&skip);
|
||||||
|
|
||||||
__ testq(rax, rax);
|
__ testq(rax, rax);
|
||||||
__ j(not_equal, &next_test);
|
__ j(not_equal, &next_test);
|
||||||
__ Drop(1); // Switch value is no longer needed.
|
__ Drop(1); // Switch value is no longer needed.
|
||||||
|
221
test/mjsunit/switch-opt.js
Normal file
221
test/mjsunit/switch-opt.js
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var result = [];
|
||||||
|
var x = 0;
|
||||||
|
|
||||||
|
function branch(b) {
|
||||||
|
if (b == "deopt") {
|
||||||
|
%DeoptimizeFunction(f);
|
||||||
|
return "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
return b ? "a" : "b";
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(label, b1, b2, b3) {
|
||||||
|
switch (label) {
|
||||||
|
case "string":
|
||||||
|
result.push(1);
|
||||||
|
break;
|
||||||
|
case branch(b1) + branch(b2):
|
||||||
|
result.push(2);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
result.push(3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
branch(b3);
|
||||||
|
result.push(4);
|
||||||
|
break;
|
||||||
|
case x++:
|
||||||
|
branch(b3);
|
||||||
|
result.push(5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertResult(r, label, b1, b2, b3) {
|
||||||
|
f(label, b1, b2, b3);
|
||||||
|
assertEquals(result, r);
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup.
|
||||||
|
assertResult([2], "aa", true, true);
|
||||||
|
assertResult([2], "ab", true, false);
|
||||||
|
assertResult([2], "ba", false, true);
|
||||||
|
assertResult([2], "bb", false, false);
|
||||||
|
assertEquals(0, x);
|
||||||
|
assertResult([4], "other");
|
||||||
|
assertEquals(1, x);
|
||||||
|
assertResult([5], 1, true, true);
|
||||||
|
assertResult([4], 1, true, true);
|
||||||
|
assertResult([5], 3, true, true);
|
||||||
|
assertResult([4], 3, true, true);
|
||||||
|
assertResult([5], 5, true, true);
|
||||||
|
assertResult([4], 5, true, true);
|
||||||
|
assertEquals(7, x);
|
||||||
|
|
||||||
|
// Test regular behavior.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([2], "aa", true, true);
|
||||||
|
assertResult([1], "string");
|
||||||
|
assertResult([4], "other");
|
||||||
|
assertEquals(8, x);
|
||||||
|
assertResult([5], 8);
|
||||||
|
assertEquals(9, x);
|
||||||
|
|
||||||
|
// Test deopt at the beginning of the case label evaluation.
|
||||||
|
assertResult([2], "ca", "deopt", true);
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4], "ca", "deopt", false);
|
||||||
|
assertEquals(10, x);
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
|
||||||
|
// Test deopt in the middle of the case label evaluation.
|
||||||
|
assertResult([2], "ac", true, "deopt");
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4], "ac", false, "deopt");
|
||||||
|
assertEquals(11, x);
|
||||||
|
|
||||||
|
// Test deopt in the default case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
print("here");
|
||||||
|
assertResult([4], 10000, false, false, "deopt");
|
||||||
|
assertEquals(12, x);
|
||||||
|
|
||||||
|
// Test deopt in the default case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4], 10000, false, false, "deopt");
|
||||||
|
assertEquals(13, x);
|
||||||
|
|
||||||
|
// Test deopt in x++ case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([5], 13, false, false, "deopt");
|
||||||
|
assertEquals(14, x);
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var result = [];
|
||||||
|
var x = 0;
|
||||||
|
|
||||||
|
function branch(b) {
|
||||||
|
if (b == "deopt") {
|
||||||
|
%DeoptimizeFunction(f);
|
||||||
|
return "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
return b ? "a" : "b";
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(label, b1, b2, b3) {
|
||||||
|
switch (label) {
|
||||||
|
case "string":
|
||||||
|
result.push(1);
|
||||||
|
break;
|
||||||
|
case branch(b1) + branch(b2):
|
||||||
|
result.push(2);
|
||||||
|
// Fall through.
|
||||||
|
case 10:
|
||||||
|
result.push(3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
branch(b3);
|
||||||
|
result.push(4);
|
||||||
|
// Fall through.
|
||||||
|
case x++:
|
||||||
|
branch(b3);
|
||||||
|
result.push(5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertResult(r, label, b1, b2, b3) {
|
||||||
|
f(label, b1, b2, b3);
|
||||||
|
assertEquals(r, result);
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup.
|
||||||
|
assertResult([2,3], "aa", true, true);
|
||||||
|
assertResult([2,3], "ab", true, false);
|
||||||
|
assertResult([2,3], "ba", false, true);
|
||||||
|
assertResult([2,3], "bb", false, false);
|
||||||
|
assertEquals(0, x);
|
||||||
|
assertResult([4,5], "other");
|
||||||
|
assertEquals(1, x);
|
||||||
|
assertResult([5], 1, true, true);
|
||||||
|
assertResult([4,5], 1, true, true);
|
||||||
|
assertResult([5], 3, true, true);
|
||||||
|
assertResult([4,5], 3, true, true);
|
||||||
|
assertResult([5], 5, true, true);
|
||||||
|
assertResult([4,5], 5, true, true);
|
||||||
|
assertEquals(7, x);
|
||||||
|
|
||||||
|
// Test regular behavior.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([2,3], "aa", true, true);
|
||||||
|
assertResult([1], "string");
|
||||||
|
assertResult([4,5], "other");
|
||||||
|
assertEquals(8, x);
|
||||||
|
assertResult([5], 8);
|
||||||
|
assertEquals(9, x);
|
||||||
|
|
||||||
|
// Test deopt at the beginning of the case label evaluation.
|
||||||
|
assertResult([2,3], "ca", "deopt", true);
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4,5], "ca", "deopt", false);
|
||||||
|
assertEquals(10, x);
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
|
||||||
|
// Test deopt in the middle of the case label evaluation.
|
||||||
|
assertResult([2,3], "ac", true, "deopt");
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4,5], "ac", false, "deopt");
|
||||||
|
assertEquals(11, x);
|
||||||
|
|
||||||
|
// Test deopt in the default case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
print("here");
|
||||||
|
assertResult([4,5], 10000, false, false, "deopt");
|
||||||
|
assertEquals(12, x);
|
||||||
|
|
||||||
|
// Test deopt in the default case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([4,5], 10000, false, false, "deopt");
|
||||||
|
assertEquals(13, x);
|
||||||
|
|
||||||
|
// Test deopt in x++ case.
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertResult([5], 13, false, false, "deopt");
|
||||||
|
assertEquals(14, x);
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user