[turbofan] Optimize certain chains of Branch into a Switch.
authorbmeurer <bmeurer@chromium.org>
Tue, 17 Feb 2015 13:29:31 +0000 (05:29 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 17 Feb 2015 13:29:46 +0000 (13:29 +0000)
This adds a new ControlFlowOptimizer that - for now - recognizes chains
of Branches generated by the SwitchBuilder for a subset of javascript
switches into Switch nodes. Those Switch nodes are then lowered to
either table or lookup switches.

Also rename Case to IfValue (and introduce IfDefault) for consistency.

BUG=v8:3872
LOG=n

Review URL: https://codereview.chromium.org/931623002

Cr-Commit-Position: refs/heads/master@{#26691}

39 files changed:
BUILD.gn
src/compiler/arm/code-generator-arm.cc
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/code-generator.h
src/compiler/common-operator.cc
src/compiler/common-operator.h
src/compiler/control-flow-optimizer.cc [new file with mode: 0644]
src/compiler/control-flow-optimizer.h [new file with mode: 0644]
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/instruction-codes.h
src/compiler/instruction-selector.cc
src/compiler/instruction-selector.h
src/compiler/opcodes.h
src/compiler/operator-properties.cc
src/compiler/operator.cc
src/compiler/operator.h
src/compiler/pipeline.cc
src/compiler/raw-machine-assembler.cc
src/compiler/raw-machine-assembler.h
src/compiler/schedule.h
src/compiler/scheduler.cc
src/compiler/simplified-lowering.cc
src/compiler/verifier.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-selector-x64.cc
src/flag-definitions.h
test/cctest/compiler/test-run-machops.cc
test/mjsunit/asm/switch.js [new file with mode: 0644]
test/unittests/compiler/common-operator-unittest.cc
test/unittests/compiler/control-flow-optimizer-unittest.cc [new file with mode: 0644]
test/unittests/compiler/graph-unittest.h
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h
test/unittests/compiler/scheduler-unittest.cc
test/unittests/unittests.gyp
tools/gyp/v8.gyp

index 2946d5ad21842befa888451940035eb5d9e5584e..2a1a8fa64122906e1445264c6f30eb53061447da 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -527,6 +527,8 @@ source_set("v8_base") {
     "src/compiler/control-builders.cc",
     "src/compiler/control-builders.h",
     "src/compiler/control-equivalence.h",
+    "src/compiler/control-flow-optimizer.cc",
+    "src/compiler/control-flow-optimizer.h",
     "src/compiler/control-reducer.cc",
     "src/compiler/control-reducer.h",
     "src/compiler/diamond.h",
index 52f0765e04306087edd13639bc8960e9f8320d01..1ff7ea3acc65e05cf0ca29420b308efcb93d6e23 100644 (file)
@@ -336,8 +336,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       AssembleArchJump(i.InputRpo(0));
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       break;
-    case kArchSwitch:
-      AssembleArchSwitch(instr);
+    case kArchLookupSwitch:
+      AssembleArchLookupSwitch(instr);
+      DCHECK_EQ(LeaveCC, i.OutputSBit());
+      break;
+    case kArchTableSwitch:
+      AssembleArchTableSwitch(instr);
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       break;
     case kArchNop:
@@ -741,18 +745,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
 }
 
 
-void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
-  ArmOperandConverter i(this, instr);
-  int const kNumLabels = static_cast<int>(instr->InputCount() - 1);
-  __ BlockConstPoolFor(kNumLabels + 2);
-  __ ldr(pc, MemOperand(pc, i.InputRegister(0), LSL, 2));
-  __ nop();
-  for (int index = 0; index < kNumLabels; ++index) {
-    __ dd(GetLabel(i.InputRpo(index + 1)));
-  }
-}
-
-
 // Assembles boolean materializations after an instruction.
 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
                                         FlagsCondition condition) {
@@ -768,6 +760,31 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
 }
 
 
+void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
+  ArmOperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  for (size_t index = 2; index < instr->InputCount(); index += 2) {
+    __ cmp(input, Operand(i.InputInt32(index + 0)));
+    __ b(eq, GetLabel(i.InputRpo(index + 1)));
+  }
+  AssembleArchJump(i.InputRpo(1));
+}
+
+
+void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
+  ArmOperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  size_t const case_count = instr->InputCount() - 2;
+  __ cmp(input, Operand(case_count));
+  __ BlockConstPoolFor(case_count + 2);
+  __ ldr(pc, MemOperand(pc, input, LSL, 2), lo);
+  __ b(GetLabel(i.InputRpo(1)));
+  for (size_t index = 0; index < case_count; ++index) {
+    __ dd(GetLabel(i.InputRpo(index + 2)));
+  }
+}
+
+
 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
       isolate(), deoptimization_id, Deoptimizer::LAZY);
index 9122359a293555a409c54fef96cd920d0b1e0224..47511a1ebf35406c3ea2d97a804ce37d4aa62a24 100644 (file)
@@ -1254,6 +1254,67 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
 }
 
 
+void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
+                                      BasicBlock** case_branches,
+                                      int32_t* case_values, size_t case_count,
+                                      int32_t min_value, int32_t max_value) {
+  ArmOperandGenerator g(this);
+  InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
+  InstructionOperand default_operand = g.Label(default_branch);
+
+  // Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
+  // is 2^31-1, so don't assume that it's non-zero below.
+  size_t value_range =
+      1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);
+
+  // Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
+  // instruction.
+  size_t table_space_cost = 4 + value_range;
+  size_t table_time_cost = 3;
+  size_t lookup_space_cost = 3 + 2 * case_count;
+  size_t lookup_time_cost = case_count;
+  if (case_count > 0 &&
+      table_space_cost + 3 * table_time_cost <=
+          lookup_space_cost + 3 * lookup_time_cost &&
+      min_value > std::numeric_limits<int32_t>::min()) {
+    InstructionOperand index_operand = value_operand;
+    if (min_value) {
+      index_operand = g.TempRegister();
+      Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I),
+           index_operand, value_operand, g.TempImmediate(min_value));
+    }
+    size_t input_count = 2 + value_range;
+    auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+    inputs[0] = index_operand;
+    std::fill(&inputs[1], &inputs[input_count], default_operand);
+    for (size_t index = 0; index < case_count; ++index) {
+      size_t value = case_values[index] - min_value;
+      BasicBlock* branch = case_branches[index];
+      DCHECK_LE(0u, value);
+      DCHECK_LT(value + 2, input_count);
+      inputs[value + 2] = g.Label(branch);
+    }
+    Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+        ->MarkAsControl();
+    return;
+  }
+
+  // Generate a sequence of conditional jumps.
+  size_t input_count = 2 + case_count * 2;
+  auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+  inputs[0] = value_operand;
+  inputs[1] = default_operand;
+  for (size_t index = 0; index < case_count; ++index) {
+    int32_t value = case_values[index];
+    BasicBlock* branch = case_branches[index];
+    inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
+    inputs[index * 2 + 2 + 1] = g.Label(branch);
+  }
+  Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+      ->MarkAsControl();
+}
+
+
 void InstructionSelector::VisitWord32Equal(Node* const node) {
   FlagsContinuation cont(kEqual, node);
   Int32BinopMatcher m(node);
index f80161678630278ecd01c2d224999f529f177c65..89c2ffb6f8fd15854f3a29e248946c1f05641c41 100644 (file)
@@ -357,8 +357,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchJmp:
       AssembleArchJump(i.InputRpo(0));
       break;
-    case kArchSwitch:
-      AssembleArchSwitch(instr);
+    case kArchTableSwitch:
+      AssembleArchTableSwitch(instr);
+      break;
+    case kArchLookupSwitch:
+      AssembleArchLookupSwitch(instr);
       break;
     case kArchNop:
       // don't emit code for nops.
@@ -841,22 +844,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
 }
 
 
-void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
-  Arm64OperandConverter i(this, instr);
-  UseScratchRegisterScope scope(masm());
-  Register reg = i.InputRegister(0);
-  Register tmp = scope.AcquireX();
-  Label table;
-  __ Adr(tmp, &table);
-  __ Add(tmp, tmp, Operand(reg, LSL, 2));
-  __ Br(tmp);
-  __ Bind(&table);
-  for (size_t index = 1; index < instr->InputCount(); ++index) {
-    __ B(GetLabel(i.InputRpo(index)));
-  }
-}
-
-
 // Assemble boolean materializations after this instruction.
 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
                                         FlagsCondition condition) {
@@ -871,6 +858,38 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
 }
 
 
+void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
+  Arm64OperandConverter i(this, instr);
+  Register input = i.InputRegister32(0);
+  for (size_t index = 2; index < instr->InputCount(); index += 2) {
+    __ Cmp(input, i.InputInt32(index + 0));
+    __ B(eq, GetLabel(i.InputRpo(index + 1)));
+  }
+  AssembleArchJump(i.InputRpo(1));
+}
+
+
+void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
+  Arm64OperandConverter i(this, instr);
+  UseScratchRegisterScope scope(masm());
+  Register input = i.InputRegister32(0);
+  Register temp = scope.AcquireX();
+  size_t const case_count = instr->InputCount() - 2;
+  Label table;
+  __ Cmp(input, case_count);
+  __ B(hs, GetLabel(i.InputRpo(1)));
+  __ Adr(temp, &table);
+  __ Add(temp, temp, Operand(input, UXTW, 2));
+  __ Br(temp);
+  __ StartBlockPools();
+  __ Bind(&table);
+  for (size_t index = 0; index < case_count; ++index) {
+    __ B(GetLabel(i.InputRpo(index + 2)));
+  }
+  __ EndBlockPools();
+}
+
+
 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
       isolate(), deoptimization_id, Deoptimizer::LAZY);
index d63b630c813199bfff0799a8005d27404d624dc8..6afd3e8c1259998e646c820fab1f2e684b9b96dd 100644 (file)
@@ -1391,6 +1391,67 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
 }
 
 
+void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
+                                      BasicBlock** case_branches,
+                                      int32_t* case_values, size_t case_count,
+                                      int32_t min_value, int32_t max_value) {
+  Arm64OperandGenerator g(this);
+  InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
+  InstructionOperand default_operand = g.Label(default_branch);
+
+  // Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
+  // is 2^31-1, so don't assume that it's non-zero below.
+  size_t value_range =
+      1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);
+
+  // Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
+  // instruction.
+  size_t table_space_cost = 4 + value_range;
+  size_t table_time_cost = 3;
+  size_t lookup_space_cost = 3 + 2 * case_count;
+  size_t lookup_time_cost = case_count;
+  if (case_count > 0 &&
+      table_space_cost + 3 * table_time_cost <=
+          lookup_space_cost + 3 * lookup_time_cost &&
+      min_value > std::numeric_limits<int32_t>::min()) {
+    InstructionOperand index_operand = value_operand;
+    if (min_value) {
+      index_operand = g.TempRegister();
+      Emit(kArm64Sub32, index_operand, value_operand,
+           g.TempImmediate(min_value));
+    }
+    size_t input_count = 2 + value_range;
+    auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+    inputs[0] = index_operand;
+    std::fill(&inputs[1], &inputs[input_count], default_operand);
+    for (size_t index = 0; index < case_count; ++index) {
+      size_t value = case_values[index] - min_value;
+      BasicBlock* branch = case_branches[index];
+      DCHECK_LE(0u, value);
+      DCHECK_LT(value + 2, input_count);
+      inputs[value + 2] = g.Label(branch);
+    }
+    Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+        ->MarkAsControl();
+    return;
+  }
+
+  // Generate a sequence of conditional jumps.
+  size_t input_count = 2 + case_count * 2;
+  auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+  inputs[0] = value_operand;
+  inputs[1] = default_operand;
+  for (size_t index = 0; index < case_count; ++index) {
+    int32_t value = case_values[index];
+    BasicBlock* branch = case_branches[index];
+    inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
+    inputs[index * 2 + 2 + 1] = g.Label(branch);
+  }
+  Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+      ->MarkAsControl();
+}
+
+
 void InstructionSelector::VisitWord32Equal(Node* const node) {
   Node* const user = node;
   FlagsContinuation cont(kEqual, node);
index 99397eae8e40e8aca2dc413e4b3f2a1501b26880..658394b3217ad79b00bfe632425f495dfd3f13e9 100644 (file)
@@ -69,9 +69,10 @@ class CodeGenerator FINAL : public GapResolver::Assembler {
 
   void AssembleArchInstruction(Instruction* instr);
   void AssembleArchJump(BasicBlock::RpoNumber target);
-  void AssembleArchSwitch(Instruction* instr);
   void AssembleArchBranch(Instruction* instr, BranchInfo* branch);
   void AssembleArchBoolean(Instruction* instr, FlagsCondition condition);
+  void AssembleArchLookupSwitch(Instruction* instr);
+  void AssembleArchTableSwitch(Instruction* instr);
 
   void AssembleDeoptimizerCall(int deoptimization_id);
 
index eda99d4fee0d2a94adc4bbc47514705ef99d64f3..8aea3df5adc85867877924a94d78aeae6928c174 100644 (file)
@@ -36,12 +36,6 @@ BranchHint BranchHintOf(const Operator* const op) {
 }
 
 
-size_t CaseIndexOf(const Operator* const op) {
-  DCHECK_EQ(IrOpcode::kCase, op->opcode());
-  return OpParameter<size_t>(op);
-}
-
-
 bool operator==(SelectParameters const& lhs, SelectParameters const& rhs) {
   return lhs.type() == rhs.type() && lhs.hint() == rhs.hint();
 }
@@ -121,6 +115,7 @@ size_t ProjectionIndexOf(const Operator* const op) {
   V(End, Operator::kKontrol, 0, 0, 1, 0, 0, 0)             \
   V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1)          \
   V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1)         \
+  V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1)       \
   V(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1)          \
   V(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1)          \
   V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
@@ -256,7 +251,7 @@ const Operator* CommonOperatorBuilder::Branch(BranchHint hint) {
 
 
 const Operator* CommonOperatorBuilder::Switch(size_t control_output_count) {
-  DCHECK_GE(control_output_count, 2u);        // Disallow trivial switches.
+  DCHECK_GE(control_output_count, 3u);        // Disallow trivial switches.
   return new (zone()) Operator(               // --
       IrOpcode::kSwitch, Operator::kKontrol,  // opcode
       "Switch",                               // name
@@ -264,12 +259,12 @@ const Operator* CommonOperatorBuilder::Switch(size_t control_output_count) {
 }
 
 
-const Operator* CommonOperatorBuilder::Case(size_t index) {
-  return new (zone()) Operator1<size_t>(    // --
-      IrOpcode::kCase, Operator::kKontrol,  // opcode
-      "Case",                               // name
-      0, 0, 1, 0, 0, 1,                     // counts
-      index);                               // parameter
+const Operator* CommonOperatorBuilder::IfValue(int32_t index) {
+  return new (zone()) Operator1<int32_t>(      // --
+      IrOpcode::kIfValue, Operator::kKontrol,  // opcode
+      "IfValue",                               // name
+      0, 0, 1, 0, 0, 1,                        // counts
+      index);                                  // parameter
 }
 
 
index 55b1c8e590c0e27cad221d94735c2d4c77cfcff2..9f2c5751630dbdcf00aa3926003e04dee36bdb3e 100644 (file)
@@ -33,9 +33,6 @@ std::ostream& operator<<(std::ostream&, BranchHint);
 BranchHint BranchHintOf(const Operator* const);
 
 
-size_t CaseIndexOf(const Operator* const);
-
-
 class SelectParameters FINAL {
  public:
   explicit SelectParameters(MachineType type,
@@ -176,7 +173,8 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
   const Operator* IfTrue();
   const Operator* IfFalse();
   const Operator* Switch(size_t control_output_count);
-  const Operator* Case(size_t index);
+  const Operator* IfValue(int32_t value);
+  const Operator* IfDefault();
   const Operator* Throw();
   const Operator* Return();
 
diff --git a/src/compiler/control-flow-optimizer.cc b/src/compiler/control-flow-optimizer.cc
new file mode 100644 (file)
index 0000000..c75d09f
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/control-flow-optimizer.h"
+
+#include "src/compiler/js-graph.h"
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/node-properties.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+ControlFlowOptimizer::ControlFlowOptimizer(JSGraph* jsgraph, Zone* zone)
+    : jsgraph_(jsgraph),
+      queue_(zone),
+      queued_(jsgraph->graph(), 2),
+      zone_(zone) {}
+
+
+void ControlFlowOptimizer::Optimize() {
+  Enqueue(graph()->start());
+  while (!queue_.empty()) {
+    Node* node = queue_.front();
+    queue_.pop();
+    if (node->IsDead()) continue;
+    switch (node->opcode()) {
+      case IrOpcode::kBranch:
+        VisitBranch(node);
+        break;
+      default:
+        VisitNode(node);
+        break;
+    }
+  }
+}
+
+
+void ControlFlowOptimizer::Enqueue(Node* node) {
+  DCHECK_NOT_NULL(node);
+  if (node->IsDead() || queued_.Get(node)) return;
+  queued_.Set(node, true);
+  queue_.push(node);
+}
+
+
+void ControlFlowOptimizer::VisitNode(Node* node) {
+  for (Node* use : node->uses()) {
+    if (NodeProperties::IsControl(use)) Enqueue(use);
+  }
+}
+
+
+void ControlFlowOptimizer::VisitBranch(Node* node) {
+  DCHECK_EQ(IrOpcode::kBranch, node->opcode());
+
+  Node* branch = node;
+  Node* cond = NodeProperties::GetValueInput(branch, 0);
+  if (cond->opcode() != IrOpcode::kWord32Equal) return VisitNode(node);
+  Int32BinopMatcher m(cond);
+  Node* index = m.left().node();
+  if (!m.right().HasValue()) return VisitNode(node);
+  int32_t value = m.right().Value();
+  ZoneSet<int32_t> values(zone());
+  values.insert(value);
+
+  Node* if_false;
+  Node* if_true;
+  while (true) {
+    // TODO(turbofan): use NodeProperties::CollectSuccessorProjections() here
+    // once available.
+    auto it = branch->uses().begin();
+    DCHECK(it != branch->uses().end());
+    if_true = *it++;
+    DCHECK(it != branch->uses().end());
+    if_false = *it++;
+    DCHECK(it == branch->uses().end());
+    if (if_true->opcode() != IrOpcode::kIfTrue) std::swap(if_true, if_false);
+    DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode());
+    DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode());
+
+    it = if_false->uses().begin();
+    if (it == if_false->uses().end()) break;
+    Node* branch1 = *it++;
+    if (branch1->opcode() != IrOpcode::kBranch) break;
+    if (it != if_false->uses().end()) break;
+    Node* cond1 = branch1->InputAt(0);
+    if (cond1->opcode() != IrOpcode::kWord32Equal) break;
+    Int32BinopMatcher m1(cond1);
+    if (m1.left().node() != index) break;
+    if (!m1.right().HasValue()) break;
+    int32_t value1 = m1.right().Value();
+    if (values.find(value1) != values.end()) break;
+    DCHECK_NE(value, value1);
+
+    if (branch != node) {
+      branch->RemoveAllInputs();
+      if_true->ReplaceInput(0, node);
+    }
+    if_true->set_op(common()->IfValue(value));
+    if_false->RemoveAllInputs();
+    Enqueue(if_true);
+
+    branch = branch1;
+    value = value1;
+    values.insert(value);
+  }
+
+  DCHECK_EQ(IrOpcode::kBranch, node->opcode());
+  DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
+  DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode());
+  DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode());
+  if (branch == node) {
+    DCHECK_EQ(1u, values.size());
+    Enqueue(if_true);
+    Enqueue(if_false);
+  } else {
+    DCHECK_LT(1u, values.size());
+    node->set_op(common()->Switch(values.size() + 1));
+    node->ReplaceInput(0, index);
+    if_true->set_op(common()->IfValue(value));
+    if_true->ReplaceInput(0, node);
+    Enqueue(if_true);
+    if_false->set_op(common()->IfDefault());
+    if_false->ReplaceInput(0, node);
+    Enqueue(if_false);
+    branch->RemoveAllInputs();
+  }
+}
+
+
+CommonOperatorBuilder* ControlFlowOptimizer::common() const {
+  return jsgraph()->common();
+}
+
+
+Graph* ControlFlowOptimizer::graph() const { return jsgraph()->graph(); }
+
+
+MachineOperatorBuilder* ControlFlowOptimizer::machine() const {
+  return jsgraph()->machine();
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/src/compiler/control-flow-optimizer.h b/src/compiler/control-flow-optimizer.h
new file mode 100644 (file)
index 0000000..fb96e01
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_CONTROL_FLOW_OPTIMIZER_H_
+#define V8_COMPILER_CONTROL_FLOW_OPTIMIZER_H_
+
+#include "src/compiler/node-marker.h"
+#include "src/zone-containers.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// Forward declarations.
+class CommonOperatorBuilder;
+class Graph;
+class JSGraph;
+class MachineOperatorBuilder;
+class Node;
+
+
+class ControlFlowOptimizer FINAL {
+ public:
+  ControlFlowOptimizer(JSGraph* jsgraph, Zone* zone);
+
+  void Optimize();
+
+ private:
+  void Enqueue(Node* node);
+  void VisitNode(Node* node);
+  void VisitBranch(Node* node);
+
+  CommonOperatorBuilder* common() const;
+  Graph* graph() const;
+  JSGraph* jsgraph() const { return jsgraph_; }
+  MachineOperatorBuilder* machine() const;
+  Zone* zone() const { return zone_; }
+
+  JSGraph* const jsgraph_;
+  ZoneQueue<Node*> queue_;
+  NodeMarker<bool> queued_;
+  Zone* const zone_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControlFlowOptimizer);
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_CONTROL_FLOW_OPTIMIZER_H_
index f82ddc3d33ba384d679384d58b609c9e8bc22b86..d20848918d30aad46f247217479f0e6b9e39544c 100644 (file)
@@ -310,8 +310,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchJmp:
       AssembleArchJump(i.InputRpo(0));
       break;
-    case kArchSwitch:
-      AssembleArchSwitch(instr);
+    case kArchLookupSwitch:
+      AssembleArchLookupSwitch(instr);
+      break;
+    case kArchTableSwitch:
+      AssembleArchTableSwitch(instr);
       break;
     case kArchNop:
       // don't emit code for nops.
@@ -761,18 +764,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
 }
 
 
-void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
-  IA32OperandConverter i(this, instr);
-  size_t const label_count = instr->InputCount() - 1;
-  Label** labels = zone()->NewArray<Label*>(label_count);
-  for (size_t index = 0; index < label_count; ++index) {
-    labels[index] = GetLabel(i.InputRpo(index + 1));
-  }
-  Label* const table = AddJumpTable(labels, label_count);
-  __ jmp(Operand::JumpTable(i.InputRegister(0), times_4, table));
-}
-
-
 // Assembles boolean materializations after an instruction.
 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
                                         FlagsCondition condition) {
@@ -851,6 +842,32 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
 }
 
 
+void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
+  IA32OperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  for (size_t index = 2; index < instr->InputCount(); index += 2) {
+    __ cmp(input, Immediate(i.InputInt32(index + 0)));
+    __ j(equal, GetLabel(i.InputRpo(index + 1)));
+  }
+  AssembleArchJump(i.InputRpo(1));
+}
+
+
+void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
+  IA32OperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  size_t const case_count = instr->InputCount() - 2;
+  Label** cases = zone()->NewArray<Label*>(case_count);
+  for (size_t index = 0; index < case_count; ++index) {
+    cases[index] = GetLabel(i.InputRpo(index + 2));
+  }
+  Label* const table = AddJumpTable(cases, case_count);
+  __ cmp(input, Immediate(case_count));
+  __ j(above_equal, GetLabel(i.InputRpo(1)));
+  __ jmp(Operand::JumpTable(input, times_4, table));
+}
+
+
 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
       isolate(), deoptimization_id, Deoptimizer::LAZY);
index da2a6594196951d33fd196a3070bda2b7958695f..beec701903ec4598f26fb3d093b7898b2ea27804 100644 (file)
@@ -928,6 +928,67 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
 }
 
 
+void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
+                                      BasicBlock** case_branches,
+                                      int32_t* case_values, size_t case_count,
+                                      int32_t min_value, int32_t max_value) {
+  IA32OperandGenerator g(this);
+  InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
+  InstructionOperand default_operand = g.Label(default_branch);
+
+  // Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
+  // is 2^31-1, so don't assume that it's non-zero below.
+  size_t value_range =
+      1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);
+
+  // Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
+  // instruction.
+  size_t table_space_cost = 4 + value_range;
+  size_t table_time_cost = 3;
+  size_t lookup_space_cost = 3 + 2 * case_count;
+  size_t lookup_time_cost = case_count;
+  if (case_count > 4 &&
+      table_space_cost + 3 * table_time_cost <=
+          lookup_space_cost + 3 * lookup_time_cost &&
+      min_value > std::numeric_limits<int32_t>::min()) {
+    InstructionOperand index_operand = value_operand;
+    if (min_value) {
+      index_operand = g.TempRegister();
+      Emit(kIA32Lea | AddressingModeField::encode(kMode_MRI), index_operand,
+           value_operand, g.TempImmediate(-min_value));
+    }
+    size_t input_count = 2 + value_range;
+    auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+    inputs[0] = index_operand;
+    std::fill(&inputs[1], &inputs[input_count], default_operand);
+    for (size_t index = 0; index < case_count; ++index) {
+      size_t value = case_values[index] - min_value;
+      BasicBlock* branch = case_branches[index];
+      DCHECK_LE(0u, value);
+      DCHECK_LT(value + 2, input_count);
+      inputs[value + 2] = g.Label(branch);
+    }
+    Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+        ->MarkAsControl();
+    return;
+  }
+
+  // Generate a sequence of conditional jumps.
+  size_t input_count = 2 + case_count * 2;
+  auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+  inputs[0] = value_operand;
+  inputs[1] = default_operand;
+  for (size_t index = 0; index < case_count; ++index) {
+    int32_t value = case_values[index];
+    BasicBlock* branch = case_branches[index];
+    inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
+    inputs[index * 2 + 2 + 1] = g.Label(branch);
+  }
+  Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+      ->MarkAsControl();
+}
+
+
 void InstructionSelector::VisitWord32Equal(Node* const node) {
   FlagsContinuation cont(kEqual, node);
   Int32BinopMatcher m(node);
index 044b15d6caa5fc7f0f5f1ce2ff804a75aac97805..ef1e942ed4707783da8d4cccb2aab423d8c242ba 100644 (file)
@@ -37,7 +37,8 @@ namespace compiler {
   V(ArchCallCodeObject)     \
   V(ArchCallJSFunction)     \
   V(ArchJmp)                \
-  V(ArchSwitch)             \
+  V(ArchLookupSwitch)       \
+  V(ArchTableSwitch)        \
   V(ArchNop)                \
   V(ArchRet)                \
   V(ArchStackPointer)       \
index e8a52a61e12213577b48711ba768ad36ac117252..c170a01d4a2bebbfe51e246254c2d86600c96ce2 100644 (file)
@@ -4,6 +4,8 @@
 
 #include "src/compiler/instruction-selector.h"
 
+#include <limits>
+
 #include "src/compiler/instruction-selector-impl.h"
 #include "src/compiler/node-matchers.h"
 #include "src/compiler/node-properties.h"
@@ -519,15 +521,33 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
     }
     case BasicBlock::kSwitch: {
       DCHECK_EQ(IrOpcode::kSwitch, input->opcode());
-      BasicBlock** const branches = &block->successors().front();
-      size_t const branch_count = block->SuccessorCount();
-      DCHECK_LE(2u, branch_count);
+      // Last successor must be Default.
+      BasicBlock* default_branch = block->successors().back();
+      DCHECK_EQ(IrOpcode::kIfDefault, default_branch->front()->opcode());
       // SSA deconstruction requires targets of branches not to have phis.
       // Edge split form guarantees this property, but is more strict.
-      for (size_t index = 0; index < branch_count; ++index) {
-        CheckNoPhis(branches[index]);
+      CheckNoPhis(default_branch);
+      // All other successors must be cases.
+      size_t case_count = block->SuccessorCount() - 1;
+      DCHECK_LE(1u, case_count);
+      BasicBlock** case_branches = &block->successors().front();
+      // Determine case values and their min/max.
+      int32_t* case_values = zone()->NewArray<int32_t>(case_count);
+      int32_t min_value = std::numeric_limits<int32_t>::max();
+      int32_t max_value = std::numeric_limits<int32_t>::min();
+      for (size_t index = 0; index < case_count; ++index) {
+        BasicBlock* branch = case_branches[index];
+        int32_t value = OpParameter<int32_t>(branch->front()->op());
+        case_values[index] = value;
+        if (min_value > value) min_value = value;
+        if (max_value < value) max_value = value;
+        // SSA deconstruction requires targets of branches not to have phis.
+        // Edge split form guarantees this property, but is more strict.
+        CheckNoPhis(branch);
       }
-      return VisitSwitch(input, branches, branch_count);
+      DCHECK_LE(min_value, max_value);
+      return VisitSwitch(input, default_branch, case_branches, case_values,
+                         case_count, min_value, max_value);
     }
     case BasicBlock::kReturn: {
       // If the result itself is a return, return its input.
@@ -561,7 +581,8 @@ MachineType InstructionSelector::GetMachineType(Node* node) {
     case IrOpcode::kIfTrue:
     case IrOpcode::kIfFalse:
     case IrOpcode::kSwitch:
-    case IrOpcode::kCase:
+    case IrOpcode::kIfValue:
+    case IrOpcode::kIfDefault:
     case IrOpcode::kEffectPhi:
     case IrOpcode::kEffectSet:
     case IrOpcode::kMerge:
@@ -701,7 +722,8 @@ void InstructionSelector::VisitNode(Node* node) {
     case IrOpcode::kIfTrue:
     case IrOpcode::kIfFalse:
     case IrOpcode::kSwitch:
-    case IrOpcode::kCase:
+    case IrOpcode::kIfValue:
+    case IrOpcode::kIfDefault:
     case IrOpcode::kEffectPhi:
     case IrOpcode::kMerge:
       // No code needed for these graph artifacts.
@@ -1058,22 +1080,6 @@ void InstructionSelector::VisitGoto(BasicBlock* target) {
 }
 
 
-void InstructionSelector::VisitSwitch(Node* node, BasicBlock** branches,
-                                      size_t branch_count) {
-  OperandGenerator g(this);
-  Node* const value = node->InputAt(0);
-  size_t const input_count = branch_count + 1;
-  InstructionOperand* const inputs =
-      zone()->NewArray<InstructionOperand>(input_count);
-  inputs[0] = g.UseRegister(value);
-  for (size_t index = 0; index < branch_count; ++index) {
-    inputs[index + 1] = g.Label(branches[index]);
-  }
-  Emit(kArchSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
-      ->MarkAsControl();
-}
-
-
 void InstructionSelector::VisitReturn(Node* value) {
   OperandGenerator g(this);
   if (value != NULL) {
index 3883b1556a2602c0fb4569c86f39b3407923e803..5c31db74e9a158dd495283fbfe3f119768850530 100644 (file)
@@ -202,7 +202,9 @@ class InstructionSelector FINAL {
   void VisitCall(Node* call);
   void VisitGoto(BasicBlock* target);
   void VisitBranch(Node* input, BasicBlock* tbranch, BasicBlock* fbranch);
-  void VisitSwitch(Node* node, BasicBlock** branches, size_t branch_count);
+  void VisitSwitch(Node* node, BasicBlock* default_branch,
+                   BasicBlock** case_branches, int32_t* case_values,
+                   size_t case_count, int32_t min_value, int32_t max_value);
   void VisitReturn(Node* value);
   void VisitThrow(Node* value);
   void VisitDeoptimize(Node* deopt);
index 2e1a9191ed359d642325611e191a551a0a5be665..a4f8d3ec1696139bff81aab66c4074e9a15e4e5c 100644 (file)
@@ -13,7 +13,8 @@
   V(IfTrue)                      \
   V(IfFalse)                     \
   V(Switch)                      \
-  V(Case)                        \
+  V(IfValue)                     \
+  V(IfDefault)                   \
   V(Merge)                       \
   V(Return)                      \
   V(OsrNormalEntry)              \
index 81299e34ffe830adce4ffa155cf126e0a962a01f..53bd16c0afbdab90cdf42f61de63a402a2dcbc3d 100644 (file)
@@ -100,7 +100,8 @@ bool OperatorProperties::IsBasicBlockBegin(const Operator* op) {
   return opcode == IrOpcode::kStart || opcode == IrOpcode::kEnd ||
          opcode == IrOpcode::kDead || opcode == IrOpcode::kLoop ||
          opcode == IrOpcode::kMerge || opcode == IrOpcode::kIfTrue ||
-         opcode == IrOpcode::kIfFalse;
+         opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfValue ||
+         opcode == IrOpcode::kIfDefault;
 }
 
 }  // namespace compiler
index e0c7ca369dafa3c0c33c4fc3ac43c0f649b8da66..ae10348422cf3eeb71525c0e637225864e9fa52e 100644 (file)
@@ -21,6 +21,10 @@ V8_INLINE N CheckRange(size_t val) {
 }  // namespace
 
 
+// static
+STATIC_CONST_MEMBER_DEFINITION const size_t Operator::kMaxControlOutputCount;
+
+
 Operator::Operator(Opcode opcode, Properties properties, const char* mnemonic,
                    size_t value_in, size_t effect_in, size_t control_in,
                    size_t value_out, size_t effect_out, size_t control_out)
@@ -32,7 +36,7 @@ Operator::Operator(Opcode opcode, Properties properties, const char* mnemonic,
       control_in_(CheckRange<uint16_t>(control_in)),
       value_out_(CheckRange<uint16_t>(value_out)),
       effect_out_(CheckRange<uint8_t>(effect_out)),
-      control_out_(CheckRange<uint8_t>(control_out)) {}
+      control_out_(CheckRange<uint16_t>(control_out)) {}
 
 
 std::ostream& operator<<(std::ostream& os, const Operator& op) {
index 616b4cf7afe178433d9db9436d458b4e8189d8d9..6407499da5515759df53363087831c4dc44c01d5 100644 (file)
@@ -85,6 +85,9 @@ class Operator : public ZoneObject {
 
   Properties properties() const { return properties_; }
 
+  // TODO(bmeurer): Use bit fields below?
+  static const size_t kMaxControlOutputCount = (1u << 16) - 1;
+
   // TODO(titzer): convert return values here to size_t.
   int ValueInputCount() const { return value_in_; }
   int EffectInputCount() const { return effect_in_; }
@@ -114,7 +117,7 @@ class Operator : public ZoneObject {
   uint16_t control_in_;
   uint16_t value_out_;
   uint8_t effect_out_;
-  uint8_t control_out_;
+  uint16_t control_out_;
 
   DISALLOW_COPY_AND_ASSIGN(Operator);
 };
index 19f0d6c45d725c0765a5ca3da3ca5a270cc1e18f..83b784f8060568978cf25515abcc31e15f756aef 100644 (file)
@@ -15,6 +15,7 @@
 #include "src/compiler/change-lowering.h"
 #include "src/compiler/code-generator.h"
 #include "src/compiler/common-operator-reducer.h"
+#include "src/compiler/control-flow-optimizer.h"
 #include "src/compiler/control-reducer.h"
 #include "src/compiler/graph-replay.h"
 #include "src/compiler/graph-visualizer.h"
@@ -544,6 +545,16 @@ struct SimplifiedLoweringPhase {
 };
 
 
+struct ControlFlowOptimizationPhase {
+  static const char* phase_name() { return "control flow optimization"; }
+
+  void Run(PipelineData* data, Zone* temp_zone) {
+    ControlFlowOptimizer optimizer(data->jsgraph(), temp_zone);
+    optimizer.Optimize();
+  }
+};
+
+
 struct ChangeLoweringPhase {
   static const char* phase_name() { return "change lowering"; }
 
@@ -961,6 +972,12 @@ Handle<Code> Pipeline::GenerateCode() {
     Run<SimplifiedLoweringPhase>();
     RunPrintAndVerify("Lowered simplified");
 
+    // Optimize control flow.
+    if (FLAG_turbo_switch) {
+      Run<ControlFlowOptimizationPhase>();
+      RunPrintAndVerify("Control flow optimized");
+    }
+
     // Lower changes that have been inserted before.
     Run<ChangeLoweringPhase>();
     // // TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
index 562f77d1445d9385eff6ccc2d0bf8c732940f030..489c2ca6d9c26b0bb002c2224535c6d2b38dc21e 100644 (file)
@@ -76,15 +76,26 @@ void RawMachineAssembler::Branch(Node* condition, Label* true_val,
 }
 
 
-void RawMachineAssembler::Switch(Node* index, Label** succ_labels,
-                                 size_t succ_count) {
+void RawMachineAssembler::Switch(Node* index, Label* default_label,
+                                 int32_t* case_values, Label** case_labels,
+                                 size_t case_count) {
   DCHECK_NE(schedule()->end(), current_block_);
-  Node* sw = NewNode(common()->Switch(succ_count), index);
+  size_t succ_count = case_count + 1;
+  Node* switch_node = NewNode(common()->Switch(succ_count), index);
   BasicBlock** succ_blocks = zone()->NewArray<BasicBlock*>(succ_count);
-  for (size_t index = 0; index < succ_count; ++index) {
-    succ_blocks[index] = Use(succ_labels[index]);
+  for (size_t index = 0; index < case_count; ++index) {
+    int32_t case_value = case_values[index];
+    BasicBlock* case_block = Use(case_labels[index]);
+    Node* case_node =
+        graph()->NewNode(common()->IfValue(case_value), switch_node);
+    schedule()->AddNode(case_block, case_node);
+    succ_blocks[index] = case_block;
   }
-  schedule()->AddSwitch(CurrentBlock(), sw, succ_blocks, succ_count);
+  BasicBlock* default_block = Use(default_label);
+  Node* default_node = graph()->NewNode(common()->IfDefault(), switch_node);
+  schedule()->AddNode(default_block, default_node);
+  succ_blocks[case_count] = default_block;
+  schedule()->AddSwitch(CurrentBlock(), switch_node, succ_blocks, succ_count);
   current_block_ = nullptr;
 }
 
index 646ace2e00e557ebd60ea92c16c8c82c131ae7ff..04b1dc6f57cebff7a001225a92e84c868abf70ec 100644 (file)
@@ -400,7 +400,8 @@ class RawMachineAssembler : public GraphBuilder {
   Label* Exit();
   void Goto(Label* label);
   void Branch(Node* condition, Label* true_val, Label* false_val);
-  void Switch(Node* index, Label** succ_labels, size_t succ_count);
+  void Switch(Node* index, Label* default_label, int32_t* case_values,
+              Label** case_labels, size_t case_count);
   // Call through CallFunctionStub with lazy deopt and frame-state.
   Node* CallFunctionStub0(Node* function, Node* receiver, Node* context,
                           Node* frame_state, CallFunctionFlags flags);
index aa479851f943107c873ddf6187ddf5af2752b007..d4d64533b5086de9350e79b00286bb2238bc684a 100644 (file)
@@ -106,6 +106,9 @@ class BasicBlock FINAL : public ZoneObject {
   Node* NodeAt(size_t index) { return nodes_[index]; }
   size_t NodeCount() const { return nodes_.size(); }
 
+  value_type& front() { return nodes_.front(); }
+  value_type const& front() const { return nodes_.front(); }
+
   typedef NodeVector::iterator iterator;
   iterator begin() { return nodes_.begin(); }
   iterator end() { return nodes_.end(); }
index 25bdba4961cc6c79fb39d716859dceccc301dc62..a21d650ed318b86623065150027120cdf98f0fc4 100644 (file)
@@ -373,13 +373,14 @@ class CFGBuilder : public ZoneObject {
   }
 
   // Collect the branch-related projections from a node, such as IfTrue,
-  // IfFalse, and Case.
+  // IfFalse, Case and Default.
   void CollectSuccessorProjections(Node* node, Node** successors,
                                    size_t successor_count) {
 #ifdef DEBUG
     DCHECK_EQ(static_cast<int>(successor_count), node->UseCount());
     std::memset(successors, 0, sizeof(*successors) * successor_count);
 #endif
+    size_t if_value_index = 0;
     for (Node* const use : node->uses()) {
       size_t index;
       switch (use->opcode()) {
@@ -394,13 +395,18 @@ class CFGBuilder : public ZoneObject {
           DCHECK_EQ(IrOpcode::kBranch, node->opcode());
           index = 1;
           break;
-        case IrOpcode::kCase:
+        case IrOpcode::kIfValue:
           DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
-          index = CaseIndexOf(use->op());
+          index = if_value_index++;
+          break;
+        case IrOpcode::kIfDefault:
+          DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
+          index = successor_count - 1;
           break;
       }
+      DCHECK_LT(if_value_index, successor_count);
       DCHECK_LT(index, successor_count);
-      DCHECK(successors[index] == nullptr);
+      DCHECK_NULL(successors[index]);
       successors[index] = use;
     }
 #ifdef DEBUG
index 4766ea2b9bccfb1a4f5c77c47c7e9c4e9582e4c4..59dd741ee476f875aa9643e0213ef3bfabe9eea1 100644 (file)
@@ -501,18 +501,14 @@ class RepresentationSelector {
       case IrOpcode::kHeapConstant:
         return VisitLeaf(node, kRepTagged);
 
-      case IrOpcode::kEnd:
-      case IrOpcode::kIfTrue:
-      case IrOpcode::kIfFalse:
-      case IrOpcode::kReturn:
-      case IrOpcode::kMerge:
-      case IrOpcode::kThrow:
-        return VisitInputs(node);  // default visit for all node inputs.
-
       case IrOpcode::kBranch:
         ProcessInput(node, 0, kRepBit);
         Enqueue(NodeProperties::GetControlInput(node, 0));
         break;
+      case IrOpcode::kSwitch:
+        ProcessInput(node, 0, kRepWord32);
+        Enqueue(NodeProperties::GetControlInput(node, 0));
+        break;
       case IrOpcode::kSelect:
         return VisitSelect(node, use, lowering);
       case IrOpcode::kPhi:
index 1cc8c9f32e7011c67b4d2f452c6fe0e6f1c3f640..26fd6933c631acddb27311355b3317079ab05983 100644 (file)
@@ -216,7 +216,8 @@ void Verifier::Visitor::Check(Node* node) {
         if (use->opcode() == IrOpcode::kIfTrue) ++count_true;
         if (use->opcode() == IrOpcode::kIfFalse) ++count_false;
       }
-      CHECK(count_true == 1 && count_false == 1);
+      CHECK_EQ(1, count_true);
+      CHECK_EQ(1, count_false);
       // Type is empty.
       CheckNotTyped(node);
       break;
@@ -229,21 +230,39 @@ void Verifier::Visitor::Check(Node* node) {
       CheckNotTyped(node);
       break;
     case IrOpcode::kSwitch: {
-      // Switch uses are Case.
-      std::vector<bool> uses;
-      uses.resize(node->UseCount());
+      // Switch uses are Case and Default.
+      int count_case = 0, count_default = 0;
       for (auto use : node->uses()) {
-        CHECK_EQ(IrOpcode::kCase, use->opcode());
-        size_t const index = CaseIndexOf(use->op());
-        CHECK_LT(index, uses.size());
-        CHECK(!uses[index]);
-        uses[index] = true;
+        switch (use->opcode()) {
+          case IrOpcode::kIfValue: {
+            for (auto user : node->uses()) {
+              if (user != use && user->opcode() == IrOpcode::kIfValue) {
+                CHECK_NE(OpParameter<int32_t>(use->op()),
+                         OpParameter<int32_t>(user->op()));
+              }
+            }
+            ++count_case;
+            break;
+          }
+          case IrOpcode::kIfDefault: {
+            ++count_default;
+            break;
+          }
+          default: {
+            UNREACHABLE();
+            break;
+          }
+        }
       }
+      CHECK_LE(1, count_case);
+      CHECK_EQ(1, count_default);
+      CHECK_EQ(node->op()->ControlOutputCount(), count_case + count_default);
       // Type is empty.
       CheckNotTyped(node);
       break;
     }
-    case IrOpcode::kCase:
+    case IrOpcode::kIfValue:
+    case IrOpcode::kIfDefault:
       CHECK_EQ(IrOpcode::kSwitch,
                NodeProperties::GetControlInput(node)->opcode());
       // Type is empty.
index e52326eac15d64825ca09233f7a6987f42ab2438..adf8966906cdba104476f3a29694f85b570661ee 100644 (file)
@@ -532,8 +532,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchJmp:
       AssembleArchJump(i.InputRpo(0));
       break;
-    case kArchSwitch:
-      AssembleArchSwitch(instr);
+    case kArchLookupSwitch:
+      AssembleArchLookupSwitch(instr);
+      break;
+    case kArchTableSwitch:
+      AssembleArchTableSwitch(instr);
       break;
     case kArchNop:
       // don't emit code for nops.
@@ -1070,19 +1073,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
 }
 
 
-void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
-  X64OperandConverter i(this, instr);
-  size_t const label_count = instr->InputCount() - 1;
-  Label** labels = zone()->NewArray<Label*>(label_count);
-  for (size_t index = 0; index < label_count; ++index) {
-    labels[index] = GetLabel(i.InputRpo(static_cast<int>(index + 1)));
-  }
-  Label* const table = AddJumpTable(labels, label_count);
-  __ leaq(kScratchRegister, Operand(table));
-  __ jmp(Operand(kScratchRegister, i.InputRegister(0), times_8, 0));
-}
-
-
 // Assembles boolean materializations after this instruction.
 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
                                         FlagsCondition condition) {
@@ -1150,6 +1140,33 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
 }
 
 
+void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
+  X64OperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  for (size_t index = 2; index < instr->InputCount(); index += 2) {
+    __ cmpl(input, Immediate(i.InputInt32(static_cast<int>(index + 0))));
+    __ j(equal, GetLabel(i.InputRpo(static_cast<int>(index + 1))));
+  }
+  AssembleArchJump(i.InputRpo(1));
+}
+
+
+void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
+  X64OperandConverter i(this, instr);
+  Register input = i.InputRegister(0);
+  int32_t const case_count = static_cast<int32_t>(instr->InputCount() - 2);
+  Label** cases = zone()->NewArray<Label*>(case_count);
+  for (int32_t index = 0; index < case_count; ++index) {
+    cases[index] = GetLabel(i.InputRpo(index + 2));
+  }
+  Label* const table = AddJumpTable(cases, case_count);
+  __ cmpl(input, Immediate(case_count));
+  __ j(above_equal, GetLabel(i.InputRpo(1)));
+  __ leaq(kScratchRegister, Operand(table));
+  __ jmp(Operand(kScratchRegister, input, times_8, 0));
+}
+
+
 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
       isolate(), deoptimization_id, Deoptimizer::LAZY);
index 1722b43ae3b819552d319de78b3d6cfe31c08f13..d3becdbc9dd2ec4ebb682b0be52de5dcd918fa2a 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
+
 #include "src/compiler/instruction-selector-impl.h"
 #include "src/compiler/node-matchers.h"
 #include "src/compiler/node-properties.h"
@@ -1151,6 +1153,70 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
 }
 
 
+void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
+                                      BasicBlock** case_branches,
+                                      int32_t* case_values, size_t case_count,
+                                      int32_t min_value, int32_t max_value) {
+  X64OperandGenerator g(this);
+  InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
+  InstructionOperand default_operand = g.Label(default_branch);
+
+  // Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
+  // is 2^31-1, so don't assume that it's non-zero below.
+  size_t value_range =
+      1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);
+
+  // Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
+  // instruction.
+  size_t table_space_cost = 4 + value_range;
+  size_t table_time_cost = 3;
+  size_t lookup_space_cost = 3 + 2 * case_count;
+  size_t lookup_time_cost = case_count;
+  if (case_count > 4 &&
+      table_space_cost + 3 * table_time_cost <=
+          lookup_space_cost + 3 * lookup_time_cost &&
+      min_value > std::numeric_limits<int32_t>::min()) {
+    InstructionOperand index_operand = g.TempRegister();
+    if (min_value) {
+      // The leal automatically zero extends, so result is a valid 64-bit index.
+      Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand,
+           value_operand, g.TempImmediate(-min_value));
+    } else {
+      // Zero extend, because we use it as 64-bit index into the jump table.
+      Emit(kX64Movl, index_operand, value_operand);
+    }
+    size_t input_count = 2 + value_range;
+    auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+    inputs[0] = index_operand;
+    std::fill(&inputs[1], &inputs[input_count], default_operand);
+    for (size_t index = 0; index < case_count; ++index) {
+      size_t value = case_values[index] - min_value;
+      BasicBlock* branch = case_branches[index];
+      DCHECK_LE(0u, value);
+      DCHECK_LT(value + 2, input_count);
+      inputs[value + 2] = g.Label(branch);
+    }
+    Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+        ->MarkAsControl();
+    return;
+  }
+
+  // Generate a sequence of conditional jumps.
+  size_t input_count = 2 + case_count * 2;
+  auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
+  inputs[0] = value_operand;
+  inputs[1] = default_operand;
+  for (size_t index = 0; index < case_count; ++index) {
+    int32_t value = case_values[index];
+    BasicBlock* branch = case_branches[index];
+    inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
+    inputs[index * 2 + 2 + 1] = g.Label(branch);
+  }
+  Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
+      ->MarkAsControl();
+}
+
+
 void InstructionSelector::VisitWord32Equal(Node* const node) {
   Node* user = node;
   FlagsContinuation cont(kEqual, node);
index a4b61aafb35966148ff8353d2529ae619e6a88c3..002fa9cdb5c452a6b89efabcdde8cfad0bf0716b 100644 (file)
@@ -424,6 +424,7 @@ DEFINE_BOOL(turbo_osr, false, "enable OSR in TurboFan")
 DEFINE_BOOL(turbo_exceptions, false, "enable exception handling in TurboFan")
 DEFINE_BOOL(turbo_stress_loop_peeling, false,
             "stress loop peeling optimization")
+DEFINE_BOOL(turbo_switch, true, "optimize switches in TurboFan")
 
 DEFINE_INT(typed_array_max_size_in_heap, 64,
            "threshold for in-heap typed array")
index 1511a01033a69b33f4063efb8de589f33703c50d..5a55ce6e23b88f74144587fbdd921f1bef14a8f4 100644 (file)
@@ -452,13 +452,17 @@ TEST(RunSwitch1) {
 
   int constant = 11223344;
 
-  MLabel block0, block1, end;
-  MLabel* cases[] = {&block0, &block1};
-  m.Switch(m.IntPtrConstant(0), cases, arraysize(cases));
+  MLabel block0, block1, def, end;
+  MLabel* case_labels[] = {&block0, &block1};
+  int32_t case_values[] = {0, 1};
+  m.Switch(m.Int32Constant(0), &def, case_values, case_labels,
+           arraysize(case_labels));
   m.Bind(&block0);
   m.Goto(&end);
   m.Bind(&block1);
   m.Goto(&end);
+  m.Bind(&def);
+  m.Goto(&end);
   m.Bind(&end);
   m.Return(m.Int32Constant(constant));
 
@@ -469,29 +473,84 @@ TEST(RunSwitch1) {
 TEST(RunSwitch2) {
   RawMachineAssemblerTester<int32_t> m(kMachInt32);
 
-  const size_t kNumCases = 255;
-  int32_t values[kNumCases];
+  MLabel blocka, blockb, blockc;
+  MLabel* case_labels[] = {&blocka, &blockb};
+  int32_t case_values[] = {std::numeric_limits<int32_t>::min(),
+                           std::numeric_limits<int32_t>::max()};
+  m.Switch(m.Parameter(0), &blockc, case_values, case_labels,
+           arraysize(case_labels));
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(-1));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(1));
+  m.Bind(&blockc);
+  m.Return(m.Int32Constant(0));
+
+  CHECK_EQ(1, m.Call(std::numeric_limits<int32_t>::max()));
+  CHECK_EQ(-1, m.Call(std::numeric_limits<int32_t>::min()));
+  for (int i = -100; i < 100; i += 25) {
+    CHECK_EQ(0, m.Call(i));
+  }
+}
+
+
+TEST(RunSwitch3) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+
+  MLabel blocka, blockb, blockc;
+  MLabel* case_labels[] = {&blocka, &blockb};
+  int32_t case_values[] = {std::numeric_limits<int32_t>::min() + 0,
+                           std::numeric_limits<int32_t>::min() + 1};
+  m.Switch(m.Parameter(0), &blockc, case_values, case_labels,
+           arraysize(case_labels));
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(0));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(1));
+  m.Bind(&blockc);
+  m.Return(m.Int32Constant(2));
+
+  CHECK_EQ(0, m.Call(std::numeric_limits<int32_t>::min() + 0));
+  CHECK_EQ(1, m.Call(std::numeric_limits<int32_t>::min() + 1));
+  for (int i = -100; i < 100; i += 25) {
+    CHECK_EQ(2, m.Call(i));
+  }
+}
+
+
+TEST(RunSwitch4) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+
+  const size_t kNumCases = 512;
+  const size_t kNumValues = kNumCases + 1;
+  int32_t values[kNumValues];
   m.main_isolate()->random_number_generator()->NextBytes(values,
                                                          sizeof(values));
-  MLabel end;
-  MLabel* cases[kNumCases];
-  Node* results[kNumCases];
+  MLabel end, def;
+  int32_t case_values[kNumCases];
+  MLabel* case_labels[kNumCases];
+  Node* results[kNumValues];
   for (size_t i = 0; i < kNumCases; ++i) {
-    cases[i] = new (m.main_zone()->New(sizeof(MLabel))) MLabel;
+    case_values[i] = static_cast<int32_t>(i);
+    case_labels[i] = new (m.main_zone()->New(sizeof(MLabel))) MLabel;
   }
-  m.Switch(m.ConvertInt32ToIntPtr(m.Parameter(0)), cases, arraysize(cases));
+  m.Switch(m.Parameter(0), &def, case_values, case_labels,
+           arraysize(case_labels));
   for (size_t i = 0; i < kNumCases; ++i) {
-    m.Bind(cases[i]);
+    m.Bind(case_labels[i]);
     results[i] = m.Int32Constant(values[i]);
     m.Goto(&end);
   }
+  m.Bind(&def);
+  results[kNumCases] = m.Int32Constant(values[kNumCases]);
+  m.Goto(&end);
   m.Bind(&end);
   const int num_results = static_cast<int>(arraysize(results));
   Node* phi =
       m.NewNode(m.common()->Phi(kMachInt32, num_results), num_results, results);
   m.Return(phi);
 
-  for (size_t i = 0; i < kNumCases; ++i) {
+  for (size_t i = 0; i < kNumValues; ++i) {
     CHECK_EQ(values[i], m.Call(static_cast<int>(i)));
   }
 }
diff --git a/test/mjsunit/asm/switch.js b/test/mjsunit/asm/switch.js
new file mode 100644 (file)
index 0000000..5cb6329
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stdlib = this;
+var foreign = {};
+var heap = new ArrayBuffer(64 * 1024);
+
+
+var switch1 = (function(stdlib, foreign, heap) {
+  "use asm";
+  function switch1(i) {
+    i = i|0;
+    switch (i) {
+      case 0: return 1;
+      case 1: return 2;
+      default: return i|0;
+    }
+  }
+  return { switch1: switch1 };
+})(stdlib, foreign, heap).switch1;
+
+assertEquals(1, switch1(0));
+assertEquals(2, switch1(1));
+for (var i = -2147483648; i < 2147483648; i += 3999773) {
+  assertEquals(i, switch1(i));
+}
+
+
+var switch2 = (function(stdlib, foreign, heap) {
+  "use asm";
+  function switch2(i) {
+    i = i|0;
+    var j = 0;
+    switch (i) {
+      case 0: j = 1; break;
+      case 1: j = 2; break;
+      case 2: j = 3; break;
+      default: j = i|0; break;
+    }
+    return j|0;
+  }
+  return { switch2: switch2 };
+})(stdlib, foreign, heap).switch2;
+
+assertEquals(1, switch2(0));
+assertEquals(2, switch2(1));
+assertEquals(3, switch2(2));
+for (var i = -2147483648; i < 2147483648; i += 3999773) {
+  assertEquals(i, switch2(i));
+}
+
+
+var switch3 = (function(stdlib, foreign, heap) {
+  "use asm";
+  function switch3(i) {
+    i = i|0;
+    var j = 0;
+    switch (i) {
+      case 0:
+      case 1: j = 1; break;
+      case 2:
+      case 3: j = 2; break;
+      case 4:
+      case 5: j = 3; break;
+      default: j = 0; break;
+    }
+    return j|0;
+  }
+  return { switch3: switch3 };
+})(stdlib, foreign, heap).switch3;
+
+assertEquals(1, switch3(0));
+assertEquals(1, switch3(1));
+assertEquals(2, switch3(2));
+assertEquals(2, switch3(3));
+assertEquals(3, switch3(4));
+assertEquals(3, switch3(5));
+for (var i = -2147483648; i < 2147483648; i += 3999773) {
+  assertEquals(0, switch3(i));
+}
+
+
+var switch4 = (function(stdlib, foreign, heap) {
+  "use asm";
+  function switch4(i) {
+    i = i|0;
+    switch (i) {
+      case -1:
+      case 1:
+        return 0;
+
+      case -2:
+      case 2:
+        return 1;
+
+      case -3:
+      case 3:
+        return 2;
+
+      case -8:
+      case 8:
+        return 3;
+
+      default:
+        return 4;
+    }
+  }
+  return { switch4: switch4 };
+})(stdlib, foreign, heap).switch4;
+
+assertEquals(4, switch4(0));
+assertEquals(0, switch4(-1));
+assertEquals(0, switch4(1));
+assertEquals(1, switch4(-2));
+assertEquals(1, switch4(2));
+assertEquals(3, switch4(-8));
+assertEquals(3, switch4(8));
+assertEquals(4, switch4(-123456789));
+assertEquals(4, switch4(123456789));
index 1f0c0fdaa4ab0b04900b701fd06e6e2d5dea7107..6e60cfd12ae228884390d8a388bc4b21c4a00155 100644 (file)
@@ -133,7 +133,7 @@ class CommonOperatorTest : public TestWithZone {
 const int kArguments[] = {1, 5, 6, 42, 100, 10000, 65000};
 
 
-const size_t kCases[] = {2, 3, 4, 100, 255};
+const size_t kCases[] = {3, 4, 100, 255, 1024, 65000};
 
 
 const float kFloatValues[] = {-std::numeric_limits<float>::infinity(),
@@ -160,6 +160,20 @@ const double kDoubleValues[] = {-std::numeric_limits<double>::infinity(),
                                 std::numeric_limits<double>::signaling_NaN()};
 
 
+const int32_t kInt32Values[] = {
+    std::numeric_limits<int32_t>::min(), -1914954528, -1698749618, -1578693386,
+    -1577976073, -1573998034, -1529085059, -1499540537, -1299205097,
+    -1090814845, -938186388, -806828902, -750927650, -520676892, -513661538,
+    -453036354, -433622833, -282638793, -28375, -27788, -22770, -18806, -14173,
+    -11956, -11200, -10212, -8160, -3751, -2758, -1522, -121, -120, -118, -117,
+    -106, -84, -80, -74, -59, -52, -48, -39, -35, -17, -11, -10, -9, -7, -5, 0,
+    9, 12, 17, 23, 29, 31, 33, 35, 40, 47, 55, 56, 62, 64, 67, 68, 69, 74, 79,
+    84, 89, 90, 97, 104, 118, 124, 126, 127, 7278, 17787, 24136, 24202, 25570,
+    26680, 30242, 32399, 420886487, 642166225, 821912648, 822577803, 851385718,
+    1212241078, 1411419304, 1589626102, 1596437184, 1876245816, 1954730266,
+    2008792749, 2045320228, std::numeric_limits<int32_t>::max()};
+
+
 const BranchHint kHints[] = {BranchHint::kNone, BranchHint::kTrue,
                              BranchHint::kFalse};
 
@@ -199,12 +213,12 @@ TEST_F(CommonOperatorTest, Switch) {
 }
 
 
-TEST_F(CommonOperatorTest, Case) {
-  TRACED_FORRANGE(size_t, index, 0, 1024) {
-    const Operator* const op = common()->Case(index);
-    EXPECT_EQ(IrOpcode::kCase, op->opcode());
+TEST_F(CommonOperatorTest, IfValue) {
+  TRACED_FOREACH(int32_t, value, kInt32Values) {
+    const Operator* const op = common()->IfValue(value);
+    EXPECT_EQ(IrOpcode::kIfValue, op->opcode());
     EXPECT_EQ(Operator::kKontrol, op->properties());
-    EXPECT_EQ(index, CaseIndexOf(op));
+    EXPECT_EQ(value, OpParameter<int32_t>(op));
     EXPECT_EQ(0, op->ValueInputCount());
     EXPECT_EQ(0, op->EffectInputCount());
     EXPECT_EQ(1, op->ControlInputCount());
diff --git a/test/unittests/compiler/control-flow-optimizer-unittest.cc b/test/unittests/compiler/control-flow-optimizer-unittest.cc
new file mode 100644 (file)
index 0000000..c083d4b
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/control-flow-optimizer.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/machine-operator.h"
+#include "test/unittests/compiler/graph-unittest.h"
+#include "test/unittests/compiler/node-test-utils.h"
+#include "testing/gmock-support.h"
+
+using testing::AllOf;
+using testing::Capture;
+using testing::CaptureEq;
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class ControlFlowOptimizerTest : public GraphTest {
+ public:
+  explicit ControlFlowOptimizerTest(int num_parameters = 3)
+      : GraphTest(num_parameters), machine_(zone()) {}
+  ~ControlFlowOptimizerTest() OVERRIDE {}
+
+ protected:
+  void Optimize() {
+    JSOperatorBuilder javascript(zone());
+    JSGraph jsgraph(isolate(), graph(), common(), &javascript, machine());
+    ControlFlowOptimizer optimizer(&jsgraph, zone());
+    optimizer.Optimize();
+  }
+
+  MachineOperatorBuilder* machine() { return &machine_; }
+
+ private:
+  MachineOperatorBuilder machine_;
+};
+
+
+TEST_F(ControlFlowOptimizerTest, Switch) {
+  Node* index = Parameter(0);
+  Node* branch0 = graph()->NewNode(
+      common()->Branch(),
+      graph()->NewNode(machine()->Word32Equal(), index, Int32Constant(0)),
+      start());
+  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
+  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
+  Node* branch1 = graph()->NewNode(
+      common()->Branch(),
+      graph()->NewNode(machine()->Word32Equal(), index, Int32Constant(1)),
+      if_false0);
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge =
+      graph()->NewNode(common()->Merge(3), if_true0, if_true1, if_false1);
+  graph()->SetEnd(graph()->NewNode(common()->End(), merge));
+  Optimize();
+  Capture<Node*> switch_capture;
+  EXPECT_THAT(end(),
+              IsEnd(IsMerge(IsIfValue(0, CaptureEq(&switch_capture)),
+                            IsIfValue(1, CaptureEq(&switch_capture)),
+                            IsIfDefault(AllOf(CaptureEq(&switch_capture),
+                                              IsSwitch(index, start()))))));
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
index af07c93a75c6d4b5b02410f4769fe2b1ee899691..21f8ebf6b494be7f57facc86c6830d04f7397018 100644 (file)
@@ -33,6 +33,8 @@ class GraphTest : public TestWithContext, public TestWithIsolateAndZone {
 
  protected:
   Node* start() { return graph()->start(); }
+  Node* end() { return graph()->end(); }
+
   Node* Parameter(int32_t index = 0);
   Node* Float32Constant(volatile float value);
   Node* Float64Constant(volatile double value);
index b709568d9b2128dfa47d12db72de18295aeaa213..eccc96227e4e0d4fbb1bd4f6e867d6d23a3a5563 100644 (file)
@@ -95,6 +95,68 @@ class IsBranchMatcher FINAL : public NodeMatcher {
 };
 
 
+class IsSwitchMatcher FINAL : public NodeMatcher {
+ public:
+  IsSwitchMatcher(const Matcher<Node*>& value_matcher,
+                  const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kSwitch),
+        value_matcher_(value_matcher),
+        control_matcher_(control_matcher) {}
+
+  void DescribeTo(std::ostream* os) const FINAL {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose value (";
+    value_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
+                                 "value", value_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<Node*> value_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
+class IsIfValueMatcher FINAL : public NodeMatcher {
+ public:
+  IsIfValueMatcher(const Matcher<int32_t>& value_matcher,
+                   const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kIfValue),
+        value_matcher_(value_matcher),
+        control_matcher_(control_matcher) {}
+
+  void DescribeTo(std::ostream* os) const FINAL {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose value (";
+    value_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(OpParameter<int32_t>(node->op()), "value",
+                                 value_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<int32_t> value_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
 class IsControl1Matcher FINAL : public NodeMatcher {
  public:
   IsControl1Matcher(IrOpcode::Value opcode,
@@ -1207,6 +1269,14 @@ Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
 }
 
 
+Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
+                       const Matcher<Node*>& control1_matcher,
+                       const Matcher<Node*>& control2_matcher) {
+  return MakeMatcher(new IsControl3Matcher(IrOpcode::kMerge, control0_matcher,
+                                           control1_matcher, control2_matcher));
+}
+
+
 Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
                       const Matcher<Node*>& control1_matcher) {
   return MakeMatcher(new IsControl2Matcher(IrOpcode::kLoop, control0_matcher,
@@ -1233,6 +1303,24 @@ Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher) {
 }
 
 
+Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher,
+                        const Matcher<Node*>& control_matcher) {
+  return MakeMatcher(new IsSwitchMatcher(value_matcher, control_matcher));
+}
+
+
+Matcher<Node*> IsIfValue(const Matcher<int32_t>& value_matcher,
+                         const Matcher<Node*>& control_matcher) {
+  return MakeMatcher(new IsIfValueMatcher(value_matcher, control_matcher));
+}
+
+
+Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher) {
+  return MakeMatcher(
+      new IsControl1Matcher(IrOpcode::kIfDefault, control_matcher));
+}
+
+
 Matcher<Node*> IsValueEffect(const Matcher<Node*>& value_matcher) {
   return MakeMatcher(new IsUnopMatcher(IrOpcode::kValueEffect, value_matcher));
 }
index 99187e4cfc27d24a6ed8416a572be7b1321e4371..03011972b7c151672f32f5481e8af4880495c0b2 100644 (file)
@@ -37,6 +37,9 @@ Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
                         const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
                        const Matcher<Node*>& control1_matcher);
+Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
+                       const Matcher<Node*>& control1_matcher,
+                       const Matcher<Node*>& control2_matcher);
 Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
                       const Matcher<Node*>& control1_matcher);
 Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
@@ -44,6 +47,11 @@ Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
                       const Matcher<Node*>& control2_matcher);
 Matcher<Node*> IsIfTrue(const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher,
+                        const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsIfValue(const Matcher<int32_t>& value_matcher,
+                         const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsValueEffect(const Matcher<Node*>& value_matcher);
 Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
                         const Matcher<Node*>& effect_matcher);
index 66496e2baf0d27b697a0970d3e459eedf966713c..860d5cd32552f425766de6504f6006a05c1b21f4 100644 (file)
@@ -1973,19 +1973,21 @@ TARGET_TEST_F(SchedulerTest, Switch) {
   graph()->SetStart(start);
 
   Node* p0 = graph()->NewNode(common()->Parameter(0), start);
-  Node* sw = graph()->NewNode(common()->Switch(2), p0, start);
-  Node* c0 = graph()->NewNode(common()->Case(0), sw);
+  Node* sw = graph()->NewNode(common()->Switch(3), p0, start);
+  Node* c0 = graph()->NewNode(common()->IfValue(0), sw);
   Node* v0 = graph()->NewNode(common()->Int32Constant(11));
-  Node* c1 = graph()->NewNode(common()->Case(1), sw);
+  Node* c1 = graph()->NewNode(common()->IfValue(1), sw);
   Node* v1 = graph()->NewNode(common()->Int32Constant(22));
-  Node* m = graph()->NewNode(common()->Merge(2), c0, c1);
-  Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 2), v0, v1, m);
+  Node* d = graph()->NewNode(common()->IfDefault(), sw);
+  Node* vd = graph()->NewNode(common()->Int32Constant(33));
+  Node* m = graph()->NewNode(common()->Merge(3), c0, c1, d);
+  Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 3), v0, v1, vd, m);
   Node* ret = graph()->NewNode(common()->Return(), phi, start, m);
   Node* end = graph()->NewNode(common()->End(), ret);
 
   graph()->SetEnd(end);
 
-  ComputeAndVerifySchedule(13, graph());
+  ComputeAndVerifySchedule(16, graph());
 }
 
 
@@ -1994,19 +1996,21 @@ TARGET_TEST_F(SchedulerTest, FloatingSwitch) {
   graph()->SetStart(start);
 
   Node* p0 = graph()->NewNode(common()->Parameter(0), start);
-  Node* sw = graph()->NewNode(common()->Switch(2), p0, start);
-  Node* c0 = graph()->NewNode(common()->Case(0), sw);
+  Node* sw = graph()->NewNode(common()->Switch(3), p0, start);
+  Node* c0 = graph()->NewNode(common()->IfValue(0), sw);
   Node* v0 = graph()->NewNode(common()->Int32Constant(11));
-  Node* c1 = graph()->NewNode(common()->Case(1), sw);
+  Node* c1 = graph()->NewNode(common()->IfValue(1), sw);
   Node* v1 = graph()->NewNode(common()->Int32Constant(22));
-  Node* m = graph()->NewNode(common()->Merge(2), c0, c1);
-  Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 2), v0, v1, m);
+  Node* d = graph()->NewNode(common()->IfDefault(), sw);
+  Node* vd = graph()->NewNode(common()->Int32Constant(33));
+  Node* m = graph()->NewNode(common()->Merge(3), c0, c1, d);
+  Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 3), v0, v1, vd, m);
   Node* ret = graph()->NewNode(common()->Return(), phi, start, start);
   Node* end = graph()->NewNode(common()->End(), ret);
 
   graph()->SetEnd(end);
 
-  ComputeAndVerifySchedule(13, graph());
+  ComputeAndVerifySchedule(16, graph());
 }
 
 }  // namespace compiler
index 28f3119f8f6381733c99630c6ad9a49726f5e2d8..a12d5e7f4bc59930232314ef74b015fee1f6469e 100644 (file)
@@ -43,6 +43,7 @@
         'compiler/common-operator-unittest.cc',
         'compiler/compiler-test-utils.h',
         'compiler/control-equivalence-unittest.cc',
+        'compiler/control-flow-optimizer-unittest.cc',
         'compiler/control-reducer-unittest.cc',
         'compiler/diamond-unittest.cc',
         'compiler/graph-reducer-unittest.cc',
index 7fa63e41e2062724057b9a2dad7542598499c95f..eef1b813f9a20deb147489dafdea40204bc5048d 100644 (file)
         '../../src/compiler/control-builders.cc',
         '../../src/compiler/control-builders.h',
         '../../src/compiler/control-equivalence.h',
+        '../../src/compiler/control-flow-optimizer.cc',
+        '../../src/compiler/control-flow-optimizer.h',
         '../../src/compiler/control-reducer.cc',
         '../../src/compiler/control-reducer.h',
         '../../src/compiler/diamond.h',