From: verwaest@chromium.org Date: Wed, 18 Dec 2013 11:44:38 +0000 (+0000) Subject: Enable optimization of functions with generic switches. X-Git-Tag: upstream/4.7.83~11353 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fb7218dc3d9ea1e031af5b6a5661d1489362aa77;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index e058a7a..dbcce96 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1025,6 +1025,16 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { 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. diff --git a/src/ast.cc b/src/ast.cc index 681b3d4..ecf83ea 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -1121,7 +1121,7 @@ CaseClause::CaseClause(Isolate* isolate, Expression* label, ZoneList* statements, int pos) - : AstNode(pos), + : Expression(isolate, pos), label_(label), statements_(statements), compare_type_(Type::None(), isolate), diff --git a/src/ast.h b/src/ast.h index 4e413c5..db7e259 100644 --- a/src/ast.h +++ b/src/ast.h @@ -115,17 +115,14 @@ namespace internal { 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 WithStatement V8_FINAL : public Statement { }; -class CaseClause V8_FINAL : public AstNode { +class CaseClause V8_FINAL : public Expression { public: DECLARE_NODE_TYPE(CaseClause) diff --git a/src/compiler.cc b/src/compiler.cc index c6dfa04..6b7786f 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -355,7 +355,6 @@ class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder { } MODULE_NODE_LIST(DEF_VISIT) DECLARATION_NODE_LIST(DEF_VISIT) - AUXILIARY_NODE_LIST(DEF_VISIT) #undef DEF_VISIT }; diff --git a/src/hydrogen.cc b/src/hydrogen.cc index d234c7b..abefcb1 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -4141,31 +4141,31 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { const int kCaseClauseLimit = 128; ZoneList* clauses = stmt->cases(); int clause_count = clauses->length(); + ZoneList 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(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( 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 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { 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 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { 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 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { compare_->set_observed_input_representation( Representation::Smi(), Representation::Smi()); compare = compare_; - } else { + } else if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) { compare = New(tag_value, label_value, Token::EQ_STRICT); + } else { + HValue* test = Add(tag_value, + label_value, + Token::EQ_STRICT); + if (test->HasObservableSideEffects()) { + Push(test); + Add(clause->id(), REMOVABLE_SIMULATE); + Drop(1); + } + compare = New(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 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // 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 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // 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()); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 6090975..e43e525 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -977,6 +977,16 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { Handle 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. diff --git a/src/rewriter.cc b/src/rewriter.cc index ba35284..5e81cf8 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -207,11 +207,6 @@ void Processor::VisitSwitchStatement(SwitchStatement* node) { } -void Processor::VisitCaseClause(CaseClause* clause) { - UNREACHABLE(); -} - - void Processor::VisitContinueStatement(ContinueStatement* node) { is_set_ = false; } diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 2affd2c..a56f223 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -986,6 +986,15 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { 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. diff --git a/test/mjsunit/switch-opt.js b/test/mjsunit/switch-opt.js new file mode 100644 index 0000000..c1d5d38 --- /dev/null +++ b/test/mjsunit/switch-opt.js @@ -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); +})();