Revision: 18347
Author: [email protected]
Date: Wed Dec 18 11:44:38 2013 UTC
Log: Enable optimization of functions with generic switches.
[email protected], [email protected]
Review URL: https://chromiumcodereview.appspot.com/110123002
http://code.google.com/p/v8/source/detail?r=18347
Added:
/branches/bleeding_edge/test/mjsunit/switch-opt.js
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/ast.cc
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/compiler.cc
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/rewriter.cc
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/switch-opt.js Wed Dec 18 11:44:38
2013 UTC
@@ -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);
+})();
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Wed Dec 18 10:40:26
2013 UTC
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Wed Dec 18 11:44:38
2013 UTC
@@ -1025,6 +1025,16 @@
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
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());
__ b(ne, &next_test);
__ Drop(1); // Switch value is no longer needed.
=======================================
--- /branches/bleeding_edge/src/ast.cc Thu Dec 12 14:57:00 2013 UTC
+++ /branches/bleeding_edge/src/ast.cc Wed Dec 18 11:44:38 2013 UTC
@@ -1121,7 +1121,7 @@
Expression* label,
ZoneList<Statement*>* statements,
int pos)
- : AstNode(pos),
+ : Expression(isolate, pos),
label_(label),
statements_(statements),
compare_type_(Type::None(), isolate),
=======================================
--- /branches/bleeding_edge/src/ast.h Thu Dec 12 14:57:00 2013 UTC
+++ /branches/bleeding_edge/src/ast.h Wed Dec 18 11:44:38 2013 UTC
@@ -115,17 +115,14 @@
V(CountOperation) \
V(BinaryOperation) \
V(CompareOperation) \
- V(ThisFunction)
-
-#define AUXILIARY_NODE_LIST(V) \
+ V(ThisFunction) \
V(CaseClause)
#define AST_NODE_LIST(V) \
DECLARATION_NODE_LIST(V) \
MODULE_NODE_LIST(V) \
STATEMENT_NODE_LIST(V) \
- EXPRESSION_NODE_LIST(V) \
- AUXILIARY_NODE_LIST(V)
+ EXPRESSION_NODE_LIST(V)
// Forward declarations
class AstConstructionVisitor;
@@ -1105,7 +1102,7 @@
};
-class CaseClause V8_FINAL : public AstNode {
+class CaseClause V8_FINAL : public Expression {
public:
DECLARE_NODE_TYPE(CaseClause)
=======================================
--- /branches/bleeding_edge/src/compiler.cc Mon Dec 9 07:41:20 2013 UTC
+++ /branches/bleeding_edge/src/compiler.cc Wed Dec 18 11:44:38 2013 UTC
@@ -355,7 +355,6 @@
}
MODULE_NODE_LIST(DEF_VISIT)
DECLARATION_NODE_LIST(DEF_VISIT)
- AUXILIARY_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
};
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Wed Dec 18 10:40:26 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc Wed Dec 18 11:44:38 2013 UTC
@@ -4141,31 +4141,31 @@
const int kCaseClauseLimit = 128;
ZoneList<CaseClause*>* clauses = stmt->cases();
int clause_count = clauses->length();
+ ZoneList<HBasicBlock*> body_blocks(clause_count, zone());
if (clause_count > kCaseClauseLimit) {
return Bailout(kSwitchStatementTooManyClauses);
}
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
- if (stmt->switch_type() == SwitchStatement::GENERIC_SWITCH) {
- return Bailout(kSwitchStatementMixedOrNonLiteralSwitchLabels);
- }
CHECK_ALIVE(VisitForValue(stmt->tag()));
Add<HSimulate>(stmt->EntryId());
- HValue* tag_value = Pop();
- HBasicBlock* first_test_block = current_block();
+ HValue* tag_value = Top();
HUnaryControlInstruction* string_check = NULL;
HBasicBlock* not_string_block = NULL;
// Test switch's tag value if all clauses are string literals
if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
- first_test_block = graph()->CreateBasicBlock();
+ HBasicBlock* first_test_block = graph()->CreateBasicBlock();
not_string_block = graph()->CreateBasicBlock();
string_check = New<HIsStringAndBranch>(
tag_value, first_test_block, not_string_block);
FinishCurrentBlock(string_check);
+ set_current_block(not_string_block);
+ Drop(1); // tag_value
+
set_current_block(first_test_block);
}
@@ -4174,7 +4174,8 @@
for (int i = 0; i < clause_count; ++i) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) {
- default_id = clause->EntryId();
+ body_blocks.Add(NULL, zone());
+ if (default_id.IsNone()) default_id = clause->EntryId();
continue;
}
@@ -4182,9 +4183,6 @@
CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop();
- HBasicBlock* next_test_block = graph()->CreateBasicBlock();
- HBasicBlock* body_block = graph()->CreateBasicBlock();
-
HControlInstruction* compare;
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
@@ -4199,22 +4197,39 @@
compare_->set_observed_input_representation(
Representation::Smi(), Representation::Smi());
compare = compare_;
- } else {
+ } else if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
compare = New<HStringCompareAndBranch>(tag_value,
label_value,
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(1, next_test_block);
FinishCurrentBlock(compare);
+ set_current_block(body_block);
+ Drop(1); // tag_value
+
set_current_block(next_test_block);
}
// Save the current block to use for the default or to join with the
// exit.
HBasicBlock* last_block = current_block();
+ Drop(1); // tag_value
if (not_string_block != NULL) {
BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
@@ -4223,7 +4238,6 @@
// 2. Loop over the clauses and the linked list of tests in lockstep,
// translating the clause bodies.
- HBasicBlock* curr_test_block = first_test_block;
HBasicBlock* fall_through_block = NULL;
BreakAndContinueInfo break_info(stmt);
@@ -4235,40 +4249,16 @@
// goes to.
HBasicBlock* normal_block = NULL;
if (clause->is_default()) {
- if (last_block != NULL) {
- normal_block = last_block;
- last_block = NULL; // Cleared to indicate we've handled it.
- }
+ if (last_block == NULL) continue;
+ normal_block = last_block;
+ last_block = NULL; // Cleared to indicate we've handled it.
} else {
- // If the current test block is deoptimizing due to an unhandled
clause
- // 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();
+ normal_block = body_blocks[i];
}
- // Identify a block to emit the body into.
- 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.
+ if (fall_through_block == NULL) {
set_current_block(normal_block);
} else {
- // (d) Reachable both ways.
HBasicBlock* join = CreateJoin(fall_through_block,
normal_block,
clause->EntryId());
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Wed Dec 18
10:40:26 2013 UTC
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Wed Dec 18
11:44:38 2013 UTC
@@ -977,6 +977,16 @@
Handle<Code> ic = CompareIC::GetUninitialized(isolate(),
Token::EQ_STRICT);
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
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);
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
=======================================
--- /branches/bleeding_edge/src/rewriter.cc Wed Nov 6 17:05:50 2013 UTC
+++ /branches/bleeding_edge/src/rewriter.cc Wed Dec 18 11:44:38 2013 UTC
@@ -205,11 +205,6 @@
}
is_set_ = is_set_ && set_after_switch;
}
-
-
-void Processor::VisitCaseClause(CaseClause* clause) {
- UNREACHABLE();
-}
void Processor::VisitContinueStatement(ContinueStatement* node) {
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Wed Dec 18 10:40:26
2013 UTC
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Wed Dec 18 11:44:38
2013 UTC
@@ -986,6 +986,15 @@
CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
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);
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.