Model exceptional edges from call nodes in TurboFan.
authormstarzinger <mstarzinger@chromium.org>
Fri, 20 Feb 2015 09:54:49 +0000 (01:54 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 20 Feb 2015 09:55:00 +0000 (09:55 +0000)
R=titzer@chromium.org,bmeurer@chromium.org

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

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

24 files changed:
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/change-lowering.cc
src/compiler/common-operator.cc
src/compiler/common-operator.h
src/compiler/control-reducer.cc
src/compiler/instruction-selector.cc
src/compiler/js-intrinsic-lowering.cc
src/compiler/js-operator.cc
src/compiler/js-typed-lowering.cc
src/compiler/node-properties.cc
src/compiler/node-properties.h
src/compiler/opcodes.h
src/compiler/operator-properties.cc
src/compiler/operator.h
src/compiler/schedule.cc
src/compiler/schedule.h
src/compiler/scheduler.cc
src/compiler/verifier.cc
test/cctest/compiler/test-run-jsexceptions.cc
test/unittests/compiler/common-operator-unittest.cc
test/unittests/compiler/js-operator-unittest.cc
test/unittests/compiler/node-properties-unittest.cc
test/unittests/compiler/schedule-unittest.cc

index 4b1fa1c..47532a5 100644 (file)
@@ -328,7 +328,12 @@ class AstGraphBuilder::ControlScopeForIteration : public ControlScope {
 class AstGraphBuilder::ControlScopeForCatch : public ControlScope {
  public:
   ControlScopeForCatch(AstGraphBuilder* owner, TryCatchBuilder* control)
-      : ControlScope(owner, 0), control_(control) {}
+      : ControlScope(owner, 0), control_(control) {
+    builder()->try_nesting_level_++;  // Increment nesting.
+  }
+  ~ControlScopeForCatch() {
+    builder()->try_nesting_level_--;  // Decrement nesting.
+  }
 
  protected:
   virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE {
@@ -354,7 +359,12 @@ class AstGraphBuilder::ControlScopeForFinally : public ControlScope {
  public:
   ControlScopeForFinally(AstGraphBuilder* owner, DeferredCommands* commands,
                          TryFinallyBuilder* control)
-      : ControlScope(owner, 0), commands_(commands), control_(control) {}
+      : ControlScope(owner, 0), commands_(commands), control_(control) {
+    builder()->try_nesting_level_++;  // Increment nesting.
+  }
+  ~ControlScopeForFinally() {
+    builder()->try_nesting_level_--;  // Decrement nesting.
+  }
 
  protected:
   virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE {
@@ -379,6 +389,7 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
       globals_(0, local_zone),
       execution_control_(nullptr),
       execution_context_(nullptr),
+      try_nesting_level_(0),
       input_buffer_size_(0),
       input_buffer_(nullptr),
       exit_control_(nullptr),
@@ -2974,6 +2985,7 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
   if (!has_context && !has_framestate && !has_control && !has_effect) {
     result = graph()->NewNode(op, value_input_count, value_inputs, incomplete);
   } else {
+    bool inside_try_scope = try_nesting_level_ > 0;
     int input_count_with_deps = value_input_count;
     if (has_context) ++input_count_with_deps;
     if (has_framestate) ++input_count_with_deps;
@@ -3001,9 +3013,24 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
     if (has_effect) {
       environment_->UpdateEffectDependency(result);
     }
-    if (result->op()->ControlOutputCount() > 0 &&
-        !environment()->IsMarkedAsUnreachable()) {
-      environment_->UpdateControlDependency(result);
+    if (!environment()->IsMarkedAsUnreachable()) {
+      // Update the current control dependency for control-producing nodes.
+      if (NodeProperties::IsControl(result)) {
+        environment_->UpdateControlDependency(result);
+      }
+      // Add implicit exception continuation for throwing nodes.
+      if (!result->op()->HasProperty(Operator::kNoThrow) && inside_try_scope) {
+        Node* on_exception = graph()->NewNode(common()->IfException(), result);
+        environment_->UpdateControlDependency(on_exception);
+        if (FLAG_turbo_exceptions) {
+          execution_control()->ThrowValue(jsgraph()->UndefinedConstant());
+        }
+      }
+      // Add implicit success continuation for throwing nodes.
+      if (!result->op()->HasProperty(Operator::kNoThrow)) {
+        Node* on_success = graph()->NewNode(common()->IfSuccess(), result);
+        environment_->UpdateControlDependency(on_success);
+      }
     }
   }
 
index ebeb6c6..c79d9c3 100644 (file)
@@ -84,6 +84,9 @@ class AstGraphBuilder : public AstVisitor {
   SetOncePointer<Node> function_closure_;
   SetOncePointer<Node> function_context_;
 
+  // Tracks how many try-blocks are currently entered.
+  int try_nesting_level_;
+
   // Temporary storage for building node input lists.
   int input_buffer_size_;
   Node** input_buffer_;
index 0057b10..6b39e92 100644 (file)
@@ -202,6 +202,7 @@ bool CanCover(Node* value, IrOpcode::Value opcode) {
   if (value->opcode() != opcode) return false;
   bool first = true;
   for (Edge const edge : value->use_edges()) {
+    if (NodeProperties::IsControlEdge(edge)) continue;
     if (NodeProperties::IsEffectEdge(edge)) continue;
     DCHECK(NodeProperties::IsValueEdge(edge));
     if (!first) return false;
index 8aea3df..5641db1 100644 (file)
@@ -115,6 +115,8 @@ 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(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1)       \
+  V(IfException, 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)          \
@@ -495,7 +497,8 @@ const Operator* CommonOperatorBuilder::Call(const CallDescriptor* descriptor) {
               Operator::ZeroIfPure(descriptor->properties()),
               Operator::ZeroIfPure(descriptor->properties()),
               descriptor->ReturnCount(),
-              Operator::ZeroIfPure(descriptor->properties()), 0, descriptor) {}
+              Operator::ZeroIfPure(descriptor->properties()),
+              Operator::ZeroIfNoThrow(descriptor->properties()), descriptor) {}
 
     void PrintParameter(std::ostream& os) const OVERRIDE {
       os << "[" << *parameter() << "]";
index 9f2c575..e74c629 100644 (file)
@@ -172,6 +172,8 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
   const Operator* Branch(BranchHint = BranchHint::kNone);
   const Operator* IfTrue();
   const Operator* IfFalse();
+  const Operator* IfSuccess();
+  const Operator* IfException();
   const Operator* Switch(size_t control_output_count);
   const Operator* IfValue(int32_t value);
   const Operator* IfDefault();
index d20c8dd..136064f 100644 (file)
@@ -131,7 +131,7 @@ class ControlReducerImpl {
           pop = false;  // restart traversing successors of this node.
           break;
         }
-        if (NodeProperties::IsControl(succ) &&
+        if (succ->op()->ControlOutputCount() > 0 &&
             !marked.IsReachableFromStart(succ)) {
           // {succ} is a control node and not yet reached from start.
           marked.Push(succ);
@@ -155,7 +155,7 @@ class ControlReducerImpl {
     // Any control nodes not reachable from start are dead, even loops.
     for (size_t i = 0; i < nodes.size(); i++) {
       Node* node = nodes[i];
-      if (NodeProperties::IsControl(node) &&
+      if (node->op()->ControlOutputCount() > 0 &&
           !marked.IsReachableFromStart(node)) {
         ReplaceNode(node, dead());  // uses will be added to revisit queue.
       }
@@ -190,16 +190,14 @@ class ControlReducerImpl {
       DCHECK(NodeProperties::IsControlEdge(edge));
       if (edge.from() == branch) continue;
       switch (edge.from()->opcode()) {
-#define CASE(Opcode) case IrOpcode::k##Opcode:
-        CONTROL_OP_LIST(CASE)
-#undef CASE
-          // Update all control nodes (except {branch}) pointing to the {loop}.
-          edge.UpdateTo(if_true);
+        case IrOpcode::kPhi:
           break;
         case IrOpcode::kEffectPhi:
           effects.push_back(edge.from());
           break;
         default:
+          // Update all control edges (except {branch}) pointing to the {loop}.
+          edge.UpdateTo(if_true);
           break;
       }
     }
index c170a01..2d0b2bd 100644 (file)
@@ -485,34 +485,34 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
 }
 
 
-namespace {
-
-V8_INLINE void CheckNoPhis(const BasicBlock* block) {
+void InstructionSelector::VisitControl(BasicBlock* block) {
 #ifdef DEBUG
-  // Branch targets should not have phis.
-  for (BasicBlock::const_iterator i = block->begin(); i != block->end(); ++i) {
-    const Node* node = *i;
-    CHECK_NE(IrOpcode::kPhi, node->opcode());
+  // SSA deconstruction requires targets of branches not to have phis.
+  // Edge split form guarantees this property, but is more strict.
+  if (block->SuccessorCount() > 1) {
+    for (BasicBlock* const successor : block->successors()) {
+      for (Node* const node : *successor) {
+        CHECK(!IrOpcode::IsPhiOpcode(node->opcode()));
+      }
+    }
   }
 #endif
-}
 
-}  // namespace
-
-
-void InstructionSelector::VisitControl(BasicBlock* block) {
   Node* input = block->control_input();
   switch (block->control()) {
     case BasicBlock::kGoto:
       return VisitGoto(block->SuccessorAt(0));
+    case BasicBlock::kCall: {
+      DCHECK_EQ(IrOpcode::kCall, input->opcode());
+      BasicBlock* success = block->SuccessorAt(0);
+      // TODO(mstarzinger): Record location of {exception} in {handler_table}.
+      // BasicBlock* exception = block->SuccessorAt(1);
+      return VisitCall(input), VisitGoto(success);
+    }
     case BasicBlock::kBranch: {
       DCHECK_EQ(IrOpcode::kBranch, input->opcode());
       BasicBlock* tbranch = block->SuccessorAt(0);
       BasicBlock* fbranch = block->SuccessorAt(1);
-      // SSA deconstruction requires targets of branches not to have phis.
-      // Edge split form guarantees this property, but is more strict.
-      CheckNoPhis(tbranch);
-      CheckNoPhis(fbranch);
       if (tbranch == fbranch) return VisitGoto(tbranch);
       // Treat special Branch(Always, IfTrue, IfFalse) as Goto(IfTrue).
       Node* const condition = input->InputAt(0);
@@ -524,9 +524,6 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
       // 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.
-      CheckNoPhis(default_branch);
       // All other successors must be cases.
       size_t case_count = block->SuccessorCount() - 1;
       DCHECK_LE(1u, case_count);
@@ -541,9 +538,6 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
         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);
       }
       DCHECK_LE(min_value, max_value);
       return VisitSwitch(input, default_branch, case_branches, case_values,
@@ -721,6 +715,8 @@ void InstructionSelector::VisitNode(Node* node) {
     case IrOpcode::kBranch:
     case IrOpcode::kIfTrue:
     case IrOpcode::kIfFalse:
+    case IrOpcode::kIfSuccess:
+    case IrOpcode::kIfException:
     case IrOpcode::kSwitch:
     case IrOpcode::kIfValue:
     case IrOpcode::kIfDefault:
index a1e6935..bbddc99 100644 (file)
@@ -145,7 +145,6 @@ Reduction JSIntrinsicLowering::ReduceInlineValueOf(Node* node) {
 
   Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
 
-
   // Replace all effect uses of {node} with the {ephi0}.
   Node* ephi0 = graph()->NewNode(ephi_op, etrue0, efalse0, merge0);
   NodeProperties::ReplaceWithValue(node, node, ephi0);
@@ -156,7 +155,7 @@ Reduction JSIntrinsicLowering::ReduceInlineValueOf(Node* node) {
 
 
 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) {
-  // Remove the effects from the node and update its effect usages.
+  // Replace all effect uses of {node} with the effect dependency.
   NodeProperties::ReplaceWithValue(node, node);
   // Remove the inputs corresponding to context, effect and control.
   NodeProperties::RemoveNonValueInputs(node);
index 72c3969..2e0de6b 100644 (file)
@@ -254,7 +254,8 @@ struct JSOperatorGlobalCache FINAL {
         : Operator(IrOpcode::kJS##Name, properties, "JS" #Name,          \
                    value_input_count, Operator::ZeroIfPure(properties),  \
                    Operator::ZeroIfPure(properties), value_output_count, \
-                   Operator::ZeroIfPure(properties), 0) {}               \
+                   Operator::ZeroIfPure(properties),                     \
+                   Operator::ZeroIfNoThrow(properties)) {}               \
   };                                                                     \
   Name##Operator k##Name##Operator;
   CACHED_OP_LIST(CACHED)
@@ -265,7 +266,7 @@ struct JSOperatorGlobalCache FINAL {
     StorePropertyOperator()
         : Operator1<LanguageMode>(IrOpcode::kJSStoreProperty,
                                   Operator::kNoProperties, "JSStoreProperty", 3,
-                                  1, 1, 0, 1, 0, kLanguageMode) {}
+                                  1, 1, 0, 1, 2, kLanguageMode) {}
   };
   StorePropertyOperator<SLOPPY> kStorePropertySloppyOperator;
   StorePropertyOperator<STRICT> kStorePropertyStrictOperator;
@@ -294,7 +295,7 @@ const Operator* JSOperatorBuilder::CallFunction(size_t arity,
   return new (zone()) Operator1<CallFunctionParameters>(   // --
       IrOpcode::kJSCallFunction, Operator::kNoProperties,  // opcode
       "JSCallFunction",                                    // name
-      parameters.arity(), 1, 1, 1, 1, 0,                   // inputs/outputs
+      parameters.arity(), 1, 1, 1, 1, 2,                   // inputs/outputs
       parameters);                                         // parameter
 }
 
@@ -307,7 +308,7 @@ const Operator* JSOperatorBuilder::CallRuntime(Runtime::FunctionId id,
   return new (zone()) Operator1<CallRuntimeParameters>(   // --
       IrOpcode::kJSCallRuntime, Operator::kNoProperties,  // opcode
       "JSCallRuntime",                                    // name
-      parameters.arity(), 1, 1, f->result_size, 1, 0,     // inputs/outputs
+      parameters.arity(), 1, 1, f->result_size, 1, 2,     // inputs/outputs
       parameters);                                        // parameter
 }
 
@@ -316,7 +317,7 @@ const Operator* JSOperatorBuilder::CallConstruct(int arguments) {
   return new (zone()) Operator1<int>(                       // --
       IrOpcode::kJSCallConstruct, Operator::kNoProperties,  // opcode
       "JSCallConstruct",                                    // name
-      arguments, 1, 1, 1, 1, 0,                             // counts
+      arguments, 1, 1, 1, 1, 2,                             // counts
       arguments);                                           // parameter
 }
 
@@ -328,7 +329,7 @@ const Operator* JSOperatorBuilder::LoadNamed(const Unique<Name>& name,
   return new (zone()) Operator1<LoadNamedParameters>(   // --
       IrOpcode::kJSLoadNamed, Operator::kNoProperties,  // opcode
       "JSLoadNamed",                                    // name
-      1, 1, 1, 1, 1, 0,                                 // counts
+      1, 1, 1, 1, 1, 2,                                 // counts
       parameters);                                      // parameter
 }
 
@@ -339,7 +340,7 @@ const Operator* JSOperatorBuilder::LoadProperty(
   return new (zone()) Operator1<LoadPropertyParameters>(   // --
       IrOpcode::kJSLoadProperty, Operator::kNoProperties,  // opcode
       "JSLoadProperty",                                    // name
-      2, 1, 1, 1, 1, 0,                                    // counts
+      2, 1, 1, 1, 1, 2,                                    // counts
       parameters);                                         // parameter
 }
 
@@ -361,7 +362,7 @@ const Operator* JSOperatorBuilder::StoreNamed(LanguageMode language_mode,
   return new (zone()) Operator1<StoreNamedParameters>(   // --
       IrOpcode::kJSStoreNamed, Operator::kNoProperties,  // opcode
       "JSStoreNamed",                                    // name
-      2, 1, 1, 0, 1, 0,                                  // counts
+      2, 1, 1, 0, 1, 2,                                  // counts
       parameters);                                       // parameter
 }
 
@@ -370,7 +371,7 @@ const Operator* JSOperatorBuilder::DeleteProperty(LanguageMode language_mode) {
   return new (zone()) Operator1<LanguageMode>(               // --
       IrOpcode::kJSDeleteProperty, Operator::kNoProperties,  // opcode
       "JSDeleteProperty",                                    // name
-      2, 1, 1, 1, 1, 0,                                      // counts
+      2, 1, 1, 1, 1, 2,                                      // counts
       language_mode);                                        // parameter
 }
 
@@ -378,21 +379,23 @@ const Operator* JSOperatorBuilder::DeleteProperty(LanguageMode language_mode) {
 const Operator* JSOperatorBuilder::LoadContext(size_t depth, size_t index,
                                                bool immutable) {
   ContextAccess access(depth, index, immutable);
-  return new (zone()) Operator1<ContextAccess>(      // --
-      IrOpcode::kJSLoadContext, Operator::kNoWrite,  // opcode
-      "JSLoadContext",                               // name
-      1, 1, 0, 1, 1, 0,                              // counts
-      access);                                       // parameter
+  return new (zone()) Operator1<ContextAccess>(  // --
+      IrOpcode::kJSLoadContext,                  // opcode
+      Operator::kNoWrite | Operator::kNoThrow,   // flags
+      "JSLoadContext",                           // name
+      1, 1, 0, 1, 1, 0,                          // counts
+      access);                                   // parameter
 }
 
 
 const Operator* JSOperatorBuilder::StoreContext(size_t depth, size_t index) {
   ContextAccess access(depth, index, false);
-  return new (zone()) Operator1<ContextAccess>(      // --
-      IrOpcode::kJSStoreContext, Operator::kNoRead,  // opcode
-      "JSStoreContext",                              // name
-      2, 1, 1, 0, 1, 0,                              // counts
-      access);                                       // parameter
+  return new (zone()) Operator1<ContextAccess>(  // --
+      IrOpcode::kJSStoreContext,                 // opcode
+      Operator::kNoRead | Operator::kNoThrow,    // flags
+      "JSStoreContext",                          // name
+      2, 1, 1, 0, 1, 0,                          // counts
+      access);                                   // parameter
 }
 
 
@@ -401,7 +404,7 @@ const Operator* JSOperatorBuilder::CreateCatchContext(
   return new (zone()) Operator1<Unique<String>>(                 // --
       IrOpcode::kJSCreateCatchContext, Operator::kNoProperties,  // opcode
       "JSCreateCatchContext",                                    // name
-      2, 1, 1, 1, 1, 0,                                          // counts
+      2, 1, 1, 1, 1, 2,                                          // counts
       name);                                                     // parameter
 }
 
index bbe46fb..5766936 100644 (file)
@@ -19,15 +19,23 @@ namespace compiler {
 // - relax effects from generic but not-side-effecting operations
 
 
-// Relax the effects of {node} by immediately replacing effect uses of {node}
-// with the effect input to {node}.
+// Relax the effects of {node} by immediately replacing effect and control uses
+// of {node} with the effect and control input to {node}.
 // TODO(turbofan): replace the effect input to {node} with {graph->start()}.
 // TODO(titzer): move into a GraphEditor?
-static void RelaxEffects(Node* node) {
+static void RelaxEffectsAndControls(Node* node) {
   NodeProperties::ReplaceWithValue(node, node, NULL);
 }
 
 
+// Relax the control uses of {node} by immediately replacing them with the
+// control input to {node}.
+// TODO(titzer): move into a GraphEditor?
+static void RelaxControls(Node* node) {
+  NodeProperties::ReplaceWithValue(node, node, node);
+}
+
+
 JSTypedLowering::JSTypedLowering(JSGraph* jsgraph, Zone* zone)
     : jsgraph_(jsgraph), simplified_(graph()->zone()), conversions_(zone) {
   zero_range_ = Type::Range(0.0, 1.0, graph()->zone());
@@ -116,9 +124,9 @@ class JSBinopReduction FINAL {
     DCHECK_EQ(0, op->ControlInputCount());
     DCHECK_EQ(2, op->ValueInputCount());
 
-    // Remove the effects from the node, if any, and update its effect usages.
+    // Remove the effects from the node, and update its effect/control usages.
     if (node_->op()->EffectInputCount() > 0) {
-      RelaxEffects(node_);
+      RelaxEffectsAndControls(node_);
     }
     // Remove the inputs corresponding to context, effect, and control.
     NodeProperties::RemoveNonValueInputs(node_);
@@ -584,7 +592,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
       DCHECK(NodeProperties::IsControl(control));
       DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
       DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
-      RelaxEffects(node);
+      RelaxEffectsAndControls(node);
       node->set_op(common()->Phi(kMachAnyTagged, input_count));
       for (int i = 0; i < input_count; ++i) {
         // We must be very careful not to introduce cycles when pushing
@@ -616,7 +624,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
       DCHECK_EQ(3, input_count);
       DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
       DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
-      RelaxEffects(node);
+      RelaxEffectsAndControls(node);
       node->set_op(common()->Select(kMachAnyTagged, input_hint));
       node->ReplaceInput(0, input->InputAt(0));
       for (int i = 1; i < input_count; ++i) {
@@ -640,7 +648,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
         NodeProperties::GetControlInput(node) != graph()->start()) {
       // JSToNumber(x:plain-primitive,context,effect,control)
       //   => JSToNumber(x,no-context,start,start)
-      RelaxEffects(node);
+      RelaxEffectsAndControls(node);
       NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
       NodeProperties::ReplaceControlInput(node, graph()->start());
       NodeProperties::ReplaceEffectInput(node, graph()->start());
@@ -796,6 +804,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
         node->ReplaceInput(3, effect);
         node->ReplaceInput(4, control);
         node->TrimInputCount(5);
+        RelaxControls(node);
         return Changed(node);
       }
       // Compute byte offset.
@@ -809,6 +818,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
       node->ReplaceInput(4, effect);
       node->ReplaceInput(5, control);
       node->TrimInputCount(6);
+      RelaxControls(node);
       return Changed(node);
     }
   }
index 47de74e..44e1448 100644 (file)
@@ -152,14 +152,18 @@ void NodeProperties::RemoveNonValueInputs(Node* node) {
 
 // static
 void NodeProperties::ReplaceWithValue(Node* node, Node* value, Node* effect) {
-  DCHECK(node->op()->ControlOutputCount() == 0);
   if (!effect && node->op()->EffectInputCount() > 0) {
     effect = NodeProperties::GetEffectInput(node);
   }
 
-  // Requires distinguishing between value and effect edges.
+  // Requires distinguishing between value, effect and control edges.
   for (Edge edge : node->use_edges()) {
-    if (IsEffectEdge(edge)) {
+    if (IsControlEdge(edge)) {
+      DCHECK_EQ(IrOpcode::kIfSuccess, edge.from()->opcode());
+      Node* control = NodeProperties::GetControlInput(node);
+      edge.from()->ReplaceUses(control);
+      edge.UpdateTo(NULL);
+    } else if (IsEffectEdge(edge)) {
       DCHECK_NOT_NULL(effect);
       edge.UpdateTo(effect);
     } else {
@@ -185,16 +189,13 @@ Node* NodeProperties::FindProjection(Node* node, size_t projection_index) {
 void NodeProperties::CollectControlProjections(Node* node, Node** projections,
                                                size_t projection_count) {
 #ifdef DEBUG
-  DCHECK_EQ(static_cast<int>(projection_count), node->UseCount());
+  DCHECK_LE(static_cast<int>(projection_count), node->UseCount());
   std::memset(projections, 0, sizeof(*projections) * projection_count);
 #endif
   size_t if_value_index = 0;
   for (Node* const use : node->uses()) {
     size_t index;
     switch (use->opcode()) {
-      default:
-        UNREACHABLE();
-      // Fall through.
       case IrOpcode::kIfTrue:
         DCHECK_EQ(IrOpcode::kBranch, node->opcode());
         index = 0;
@@ -203,6 +204,14 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
         DCHECK_EQ(IrOpcode::kBranch, node->opcode());
         index = 1;
         break;
+      case IrOpcode::kIfSuccess:
+        DCHECK_EQ(IrOpcode::kCall, node->opcode());
+        index = 0;
+        break;
+      case IrOpcode::kIfException:
+        DCHECK_EQ(IrOpcode::kCall, node->opcode());
+        index = 1;
+        break;
       case IrOpcode::kIfValue:
         DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
         index = if_value_index++;
@@ -211,6 +220,8 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
         DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
         index = projection_count - 1;
         break;
+      default:
+        continue;
     }
     DCHECK_LT(if_value_index, projection_count);
     DCHECK_LT(index, projection_count);
index a13eea3..47396fb 100644 (file)
@@ -81,7 +81,8 @@ class NodeProperties FINAL {
   static void RemoveNonValueInputs(Node* node);
 
   // Replace value uses of {node} with {value} and effect uses of {node} with
-  // {effect}. If {effect == NULL}, then use the effect input to {node}.
+  // {effect}. If {effect == NULL}, then use the effect input to {node}. All
+  // control uses will be relaxed assuming {node} cannot throw.
   static void ReplaceWithValue(Node* node, Node* value, Node* effect = nullptr);
 
 
@@ -91,8 +92,9 @@ class NodeProperties FINAL {
   static Node* FindProjection(Node* node, size_t projection_index);
 
   // Collect the branch-related projections from a node, such as IfTrue,
-  // IfFalse, IfValue and IfDefault.
+  // IfFalse, IfSuccess, IfException, IfValue and IfDefault.
   //  - Branch: [ IfTrue, IfFalse ]
+  //  - Call  : [ IfSuccess, IfException ]
   //  - Switch: [ IfValue, ..., IfDefault ]
   static void CollectControlProjections(Node* node, Node** proj, size_t count);
 
index a4f8d3e..31ef304 100644 (file)
@@ -12,6 +12,8 @@
   V(Branch)                      \
   V(IfTrue)                      \
   V(IfFalse)                     \
+  V(IfSuccess)                   \
+  V(IfException)                 \
   V(Switch)                      \
   V(IfValue)                     \
   V(IfDefault)                   \
@@ -299,8 +301,12 @@ class IrOpcode {
     return kInt32Constant <= value && value <= kHeapConstant;
   }
 
-  static bool IsPhiOpcode(Value val) {
-    return val == kPhi || val == kEffectPhi;
+  static bool IsPhiOpcode(Value value) {
+    return value == kPhi || value == kEffectPhi;
+  }
+
+  static bool IsMergeOpcode(Value value) {
+    return value == kMerge || value == kLoop;
   }
 };
 
index 53bd16c..64f384b 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::kIfValue ||
+         opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfSuccess ||
+         opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue ||
          opcode == IrOpcode::kIfDefault;
 }
 
index 6407499..114cf6a 100644 (file)
@@ -97,6 +97,10 @@ class Operator : public ZoneObject {
   int EffectOutputCount() const { return effect_out_; }
   int ControlOutputCount() const { return control_out_; }
 
+  static size_t ZeroIfNoThrow(Properties properties) {
+    return (properties & kNoThrow) == kNoThrow ? 0 : 2;
+  }
+
   static size_t ZeroIfPure(Properties properties) {
     return (properties & kPure) == kPure ? 0 : 1;
   }
index 8924ae5..ec0caf0 100644 (file)
@@ -100,6 +100,8 @@ std::ostream& operator<<(std::ostream& os, const BasicBlock::Control& c) {
       return os << "none";
     case BasicBlock::kGoto:
       return os << "goto";
+    case BasicBlock::kCall:
+      return os << "call";
     case BasicBlock::kBranch:
       return os << "branch";
     case BasicBlock::kSwitch:
@@ -194,16 +196,27 @@ void Schedule::AddNode(BasicBlock* block, Node* node) {
 
 
 void Schedule::AddGoto(BasicBlock* block, BasicBlock* succ) {
-  DCHECK(block->control() == BasicBlock::kNone);
+  DCHECK_EQ(BasicBlock::kNone, block->control());
   block->set_control(BasicBlock::kGoto);
   AddSuccessor(block, succ);
 }
 
 
+void Schedule::AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
+                       BasicBlock* exception_block) {
+  DCHECK_EQ(BasicBlock::kNone, block->control());
+  DCHECK_EQ(IrOpcode::kCall, call->opcode());
+  block->set_control(BasicBlock::kCall);
+  AddSuccessor(block, success_block);
+  AddSuccessor(block, exception_block);
+  SetControlInput(block, call);
+}
+
+
 void Schedule::AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
                          BasicBlock* fblock) {
-  DCHECK(block->control() == BasicBlock::kNone);
-  DCHECK(branch->opcode() == IrOpcode::kBranch);
+  DCHECK_EQ(BasicBlock::kNone, block->control());
+  DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
   block->set_control(BasicBlock::kBranch);
   AddSuccessor(block, tblock);
   AddSuccessor(block, fblock);
@@ -224,7 +237,7 @@ void Schedule::AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks,
 
 
 void Schedule::AddReturn(BasicBlock* block, Node* input) {
-  DCHECK(block->control() == BasicBlock::kNone);
+  DCHECK_EQ(BasicBlock::kNone, block->control());
   block->set_control(BasicBlock::kReturn);
   SetControlInput(block, input);
   if (block != end()) AddSuccessor(block, end());
@@ -232,7 +245,7 @@ void Schedule::AddReturn(BasicBlock* block, Node* input) {
 
 
 void Schedule::AddThrow(BasicBlock* block, Node* input) {
-  DCHECK(block->control() == BasicBlock::kNone);
+  DCHECK_EQ(BasicBlock::kNone, block->control());
   block->set_control(BasicBlock::kThrow);
   SetControlInput(block, input);
   if (block != end()) AddSuccessor(block, end());
@@ -241,8 +254,8 @@ void Schedule::AddThrow(BasicBlock* block, Node* input) {
 
 void Schedule::InsertBranch(BasicBlock* block, BasicBlock* end, Node* branch,
                             BasicBlock* tblock, BasicBlock* fblock) {
-  DCHECK(block->control() != BasicBlock::kNone);
-  DCHECK(end->control() == BasicBlock::kNone);
+  DCHECK_NE(BasicBlock::kNone, block->control());
+  DCHECK_EQ(BasicBlock::kNone, end->control());
   end->set_control(block->control());
   block->set_control(BasicBlock::kBranch);
   MoveSuccessors(block, end);
index d4d6453..d46825e 100644 (file)
@@ -32,6 +32,7 @@ class BasicBlock FINAL : public ZoneObject {
   enum Control {
     kNone,    // Control not initialized yet.
     kGoto,    // Goto a single successor block.
+    kCall,    // Call with continuation as first successor, exception second.
     kBranch,  // Branch if true to first successor, otherwise second.
     kSwitch,  // Table dispatch to one of the successor blocks.
     kReturn,  // Return a value from this method.
@@ -233,6 +234,10 @@ class Schedule FINAL : public ZoneObject {
   // BasicBlock building: add a goto to the end of {block}.
   void AddGoto(BasicBlock* block, BasicBlock* succ);
 
+  // BasicBlock building: add a call at the end of {block}.
+  void AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
+               BasicBlock* exception_block);
+
   // BasicBlock building: add a branch at the end of {block}.
   void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
                  BasicBlock* fblock);
index 6e105e3..8cbc05e 100644 (file)
@@ -286,7 +286,7 @@ class CFGBuilder : public ZoneObject {
   }
 
  private:
-  // TODO(mstarzinger): Only for Scheduler::FuseFloatingControl.
+  friend class ScheduleLateNodeVisitor;
   friend class Scheduler;
 
   void FixNode(BasicBlock* block, Node* node) {
@@ -320,6 +320,11 @@ class CFGBuilder : public ZoneObject {
       case IrOpcode::kSwitch:
         BuildBlocksForSuccessors(node);
         break;
+      case IrOpcode::kCall:
+        if (IsExceptionalCall(node)) {
+          BuildBlocksForSuccessors(node);
+        }
+        break;
       default:
         break;
     }
@@ -347,6 +352,12 @@ class CFGBuilder : public ZoneObject {
         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
         ConnectThrow(node);
         break;
+      case IrOpcode::kCall:
+        if (IsExceptionalCall(node)) {
+          scheduler_->UpdatePlacement(node, Scheduler::kFixed);
+          ConnectCall(node);
+        }
+        break;
       default:
         break;
     }
@@ -381,6 +392,31 @@ class CFGBuilder : public ZoneObject {
     }
   }
 
+  BasicBlock* FindPredecessorBlock(Node* node) {
+    BasicBlock* predecessor_block = nullptr;
+    while (true) {
+      predecessor_block = schedule_->block(node);
+      if (predecessor_block != nullptr) break;
+      node = NodeProperties::GetControlInput(node);
+    }
+    return predecessor_block;
+  }
+
+  void ConnectCall(Node* call) {
+    BasicBlock* successor_blocks[2];
+    CollectSuccessorBlocks(call, successor_blocks, arraysize(successor_blocks));
+
+    // Consider the exception continuation to be deferred.
+    successor_blocks[1]->set_deferred(true);
+
+    Node* call_control = NodeProperties::GetControlInput(call);
+    BasicBlock* call_block = FindPredecessorBlock(call_control);
+    TraceConnect(call, call_block, successor_blocks[0]);
+    TraceConnect(call, call_block, successor_blocks[1]);
+    schedule_->AddCall(call_block, call, successor_blocks[0],
+                       successor_blocks[1]);
+  }
+
   void ConnectBranch(Node* branch) {
     BasicBlock* successor_blocks[2];
     CollectSuccessorBlocks(branch, successor_blocks,
@@ -404,10 +440,8 @@ class CFGBuilder : public ZoneObject {
       schedule_->InsertBranch(component_start_, component_end_, branch,
                               successor_blocks[0], successor_blocks[1]);
     } else {
-      Node* branch_block_node = NodeProperties::GetControlInput(branch);
-      BasicBlock* branch_block = schedule_->block(branch_block_node);
-      DCHECK_NOT_NULL(branch_block);
-
+      Node* branch_control = NodeProperties::GetControlInput(branch);
+      BasicBlock* branch_block = FindPredecessorBlock(branch_control);
       TraceConnect(branch, branch_block, successor_blocks[0]);
       TraceConnect(branch, branch_block, successor_blocks[1]);
       schedule_->AddBranch(branch_block, branch, successor_blocks[0],
@@ -428,14 +462,12 @@ class CFGBuilder : public ZoneObject {
       schedule_->InsertSwitch(component_start_, component_end_, sw,
                               successor_blocks, successor_count);
     } else {
-      Node* sw_block_node = NodeProperties::GetControlInput(sw);
-      BasicBlock* sw_block = schedule_->block(sw_block_node);
-      DCHECK_NOT_NULL(sw_block);
-
+      Node* switch_control = NodeProperties::GetControlInput(sw);
+      BasicBlock* switch_block = FindPredecessorBlock(switch_control);
       for (size_t index = 0; index < successor_count; ++index) {
-        TraceConnect(sw, sw_block, successor_blocks[index]);
+        TraceConnect(sw, switch_block, successor_blocks[index]);
       }
-      schedule_->AddSwitch(sw_block, sw, successor_blocks, successor_count);
+      schedule_->AddSwitch(switch_block, sw, successor_blocks, successor_count);
     }
   }
 
@@ -448,22 +480,22 @@ class CFGBuilder : public ZoneObject {
     // For all of the merge's control inputs, add a goto at the end to the
     // merge's basic block.
     for (Node* const input : merge->inputs()) {
-      BasicBlock* predecessor_block = schedule_->block(input);
+      BasicBlock* predecessor_block = FindPredecessorBlock(input);
       TraceConnect(merge, predecessor_block, block);
       schedule_->AddGoto(predecessor_block, block);
     }
   }
 
   void ConnectReturn(Node* ret) {
-    Node* return_block_node = NodeProperties::GetControlInput(ret);
-    BasicBlock* return_block = schedule_->block(return_block_node);
+    Node* return_control = NodeProperties::GetControlInput(ret);
+    BasicBlock* return_block = FindPredecessorBlock(return_control);
     TraceConnect(ret, return_block, NULL);
     schedule_->AddReturn(return_block, ret);
   }
 
   void ConnectThrow(Node* thr) {
-    Node* throw_block_node = NodeProperties::GetControlInput(thr);
-    BasicBlock* throw_block = schedule_->block(throw_block_node);
+    Node* throw_control = NodeProperties::GetControlInput(thr);
+    BasicBlock* throw_block = FindPredecessorBlock(throw_control);
     TraceConnect(thr, throw_block, NULL);
     schedule_->AddThrow(throw_block, thr);
   }
@@ -479,6 +511,13 @@ class CFGBuilder : public ZoneObject {
     }
   }
 
+  bool IsExceptionalCall(Node* node) {
+    for (Node* const use : node->uses()) {
+      if (use->opcode() == IrOpcode::kIfException) return true;
+    }
+    return false;
+  }
+
   bool IsFinalMerge(Node* node) {
     return (node->opcode() == IrOpcode::kMerge &&
             node == scheduler_->graph_->end()->InputAt(0));
@@ -1324,7 +1363,7 @@ class ScheduleLateNodeVisitor {
     }
 
     // Schedule the node or a floating control structure.
-    if (NodeProperties::IsControl(node)) {
+    if (IrOpcode::IsMergeOpcode(node->opcode())) {
       ScheduleFloatingControl(block, node);
     } else {
       ScheduleNode(block, node);
@@ -1448,10 +1487,13 @@ class ScheduleLateNodeVisitor {
     return block;
   }
 
+  BasicBlock* FindPredecessorBlock(Node* node) {
+    return scheduler_->control_flow_builder_->FindPredecessorBlock(node);
+  }
+
   BasicBlock* GetBlockForUse(Edge edge) {
     Node* use = edge.from();
-    IrOpcode::Value opcode = use->opcode();
-    if (IrOpcode::IsPhiOpcode(opcode)) {
+    if (IrOpcode::IsPhiOpcode(use->opcode())) {
       // If the use is from a coupled (i.e. floating) phi, compute the common
       // dominator of its uses. This will not recurse more than one level.
       if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
@@ -1460,15 +1502,23 @@ class ScheduleLateNodeVisitor {
         DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use));
         return GetCommonDominatorOfUses(use);
       }
-      // If the use is from a fixed (i.e. non-floating) phi, use the block
-      // of the corresponding control input to the merge.
+      // If the use is from a fixed (i.e. non-floating) phi, we use the
+      // predecessor block of the corresponding control input to the merge.
       if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
         Trace("  input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(),
               use->op()->mnemonic());
         Node* merge = NodeProperties::GetControlInput(use, 0);
-        opcode = merge->opcode();
-        DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop);
-        use = NodeProperties::GetControlInput(merge, edge.index());
+        DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
+        Node* input = NodeProperties::GetControlInput(merge, edge.index());
+        return FindPredecessorBlock(input);
+      }
+    } else if (IrOpcode::IsMergeOpcode(use->opcode())) {
+      // If the use is from a fixed (i.e. non-floating) merge, we use the
+      // predecessor block of the current input to the merge.
+      if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
+        Trace("  input@%d into a fixed merge #%d:%s\n", edge.index(), use->id(),
+              use->op()->mnemonic());
+        return FindPredecessorBlock(edge.to());
       }
     }
     BasicBlock* result = schedule_->block(use);
index 9480afb..fe19eaa 100644 (file)
@@ -228,6 +228,15 @@ void Verifier::Visitor::Check(Node* node) {
       // Type is empty.
       CheckNotTyped(node);
       break;
+    case IrOpcode::kIfSuccess:
+    case IrOpcode::kIfException: {
+      // IfSuccess and IfException continuation only on throwing nodes.
+      Node* input = NodeProperties::GetControlInput(node, 0);
+      CHECK(!input->op()->HasProperty(Operator::kNoThrow));
+      // Type is empty.
+      CheckNotTyped(node);
+      break;
+    }
     case IrOpcode::kSwitch: {
       // Switch uses are Case and Default.
       int count_case = 0, count_default = 0;
index 74990da..7610962 100644 (file)
@@ -118,6 +118,28 @@ TEST(CatchBreak) {
 }
 
 
+TEST(CatchCall) {
+  i::FLAG_turbo_exceptions = true;
+  const char* src =
+      "(function(fun) {"
+      "  var r = '-';"
+      "  try {"
+      "    r += 'A-';"
+      "    fun();"
+      "  } catch (e) {"
+      "    r += e;"
+      "  }"
+      "  return r;"
+      "})";
+  FunctionTester T(src);
+
+  CompileRun("function thrower() { throw 'T-'; }");
+#if 0  // TODO(mstarzinger): Enable once we have exception handlers.
+  T.CheckCall(T.Val("-A-T-"), T.NewFunction("thrower"));
+#endif
+}
+
+
 TEST(Finally) {
   i::FLAG_turbo_exceptions = true;
   const char* src =
index 6e60cfd..c0d25ea 100644 (file)
@@ -53,6 +53,8 @@ const SharedOperator kSharedOperators[] = {
     SHARED(End, Operator::kKontrol, 0, 0, 1, 0, 0, 0),
     SHARED(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
     SHARED(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
+    SHARED(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
+    SHARED(IfException, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
     SHARED(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1),
     SHARED(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1)
 #undef SHARED
index 8f4622a..ef0a4df 100644 (file)
@@ -28,6 +28,7 @@ struct SharedOperator {
   int control_input_count;
   int value_output_count;
   int effect_output_count;
+  int control_output_count;
 };
 
 
@@ -39,48 +40,49 @@ std::ostream& operator<<(std::ostream& os, const SharedOperator& sop) {
 const SharedOperator kSharedOperators[] = {
 #define SHARED(Name, properties, value_input_count, frame_state_input_count, \
                effect_input_count, control_input_count, value_output_count,  \
-               effect_output_count)                                          \
+               effect_output_count, control_output_count)                    \
   {                                                                          \
     &JSOperatorBuilder::Name, IrOpcode::kJS##Name, properties,               \
         value_input_count, frame_state_input_count, effect_input_count,      \
-        control_input_count, value_output_count, effect_output_count         \
+        control_input_count, value_output_count, effect_output_count,        \
+        control_output_count                                                 \
   }
-    SHARED(Equal, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(StrictEqual, Operator::kPure, 2, 0, 0, 0, 1, 0),
-    SHARED(StrictNotEqual, Operator::kPure, 2, 0, 0, 0, 1, 0),
-    SHARED(LessThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(GreaterThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(LessThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(GreaterThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(BitwiseOr, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(BitwiseXor, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(BitwiseAnd, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(ShiftLeft, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(ShiftRight, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(ShiftRightLogical, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Add, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Subtract, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Multiply, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Divide, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Modulus, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(UnaryNot, Operator::kPure, 1, 0, 0, 0, 1, 0),
-    SHARED(ToBoolean, Operator::kPure, 1, 0, 0, 0, 1, 0),
-    SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
-    SHARED(ToString, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
-    SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
-    SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
-    SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
-    SHARED(Create, Operator::kEliminatable, 0, 0, 1, 1, 1, 1),
-    SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(TypeOf, Operator::kPure, 1, 0, 0, 0, 1, 0),
-    SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(Debugger, Operator::kNoProperties, 0, 0, 1, 1, 0, 1),
-    SHARED(CreateFunctionContext, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
-    SHARED(CreateWithContext, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
-    SHARED(CreateBlockContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1),
-    SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1),
-    SHARED(CreateScriptContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1)
+    SHARED(Equal, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(StrictEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
+    SHARED(StrictNotEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
+    SHARED(LessThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(GreaterThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(LessThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(GreaterThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(BitwiseOr, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(BitwiseXor, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(BitwiseAnd, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(ShiftLeft, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(ShiftRight, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(ShiftRightLogical, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Add, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Subtract, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Multiply, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Divide, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Modulus, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(UnaryNot, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
+    SHARED(ToBoolean, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
+    SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
+    SHARED(ToString, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
+    SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
+    SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
+    SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
+    SHARED(Create, Operator::kEliminatable, 0, 0, 1, 1, 1, 1, 0),
+    SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(TypeOf, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
+    SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(Debugger, Operator::kNoProperties, 0, 0, 1, 1, 0, 1, 2),
+    SHARED(CreateFunctionContext, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
+    SHARED(CreateWithContext, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
+    SHARED(CreateBlockContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
+    SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
+    SHARED(CreateScriptContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2)
 #undef SHARED
 };
 
@@ -122,7 +124,7 @@ TEST_P(JSSharedOperatorTest, NumberOfInputsAndOutputs) {
 
   EXPECT_EQ(sop.value_output_count, op->ValueOutputCount());
   EXPECT_EQ(sop.effect_output_count, op->EffectOutputCount());
-  EXPECT_EQ(0, op->ControlOutputCount());
+  EXPECT_EQ(sop.control_output_count, op->ControlOutputCount());
 }
 
 
@@ -181,7 +183,7 @@ TEST_P(JSStorePropertyOperatorTest, NumberOfInputsAndOutputs) {
 
   EXPECT_EQ(0, op->ValueOutputCount());
   EXPECT_EQ(1, op->EffectOutputCount());
-  EXPECT_EQ(0, op->ControlOutputCount());
+  EXPECT_EQ(2, op->ControlOutputCount());
 }
 
 
index bb471bd..f4ce0d3 100644 (file)
@@ -8,6 +8,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 
 using testing::AnyOf;
+using testing::ElementsAre;
 using testing::IsNull;
 
 namespace v8 {
@@ -17,6 +18,62 @@ namespace compiler {
 typedef TestWithZone NodePropertiesTest;
 
 
+namespace {
+
+const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
+                             "MockOperator", 0, 0, 0, 1, 0, 0);
+const Operator kMockOpEffect(IrOpcode::kDead, Operator::kNoProperties,
+                             "MockOpEffect", 0, 1, 0, 1, 1, 0);
+const Operator kMockOpControl(IrOpcode::kDead, Operator::kNoProperties,
+                              "MockOpControl", 0, 0, 1, 1, 0, 1);
+
+}  // namespace
+
+
+TEST_F(NodePropertiesTest, ReplaceWithValue_ValueUse) {
+  CommonOperatorBuilder common(zone());
+  Node* node = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
+  Node* use_value = Node::New(zone(), 0, common.Return(), 1, &node, false);
+  Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
+  NodeProperties::ReplaceWithValue(node, replacement);
+  EXPECT_EQ(replacement, use_value->InputAt(0));
+  EXPECT_EQ(0, node->UseCount());
+  EXPECT_EQ(1, replacement->UseCount());
+  EXPECT_THAT(replacement->uses(), ElementsAre(use_value));
+}
+
+
+TEST_F(NodePropertiesTest, ReplaceWithValue_EffectUse) {
+  CommonOperatorBuilder common(zone());
+  Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
+  Node* node = Node::New(zone(), 0, &kMockOpEffect, 1, &start, false);
+  Node* use_effect = Node::New(zone(), 0, common.EffectPhi(1), 1, &node, false);
+  Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
+  NodeProperties::ReplaceWithValue(node, replacement);
+  EXPECT_EQ(start, use_effect->InputAt(0));
+  EXPECT_EQ(0, node->UseCount());
+  EXPECT_EQ(2, start->UseCount());
+  EXPECT_EQ(0, replacement->UseCount());
+  EXPECT_THAT(start->uses(), ElementsAre(node, use_effect));
+}
+
+
+TEST_F(NodePropertiesTest, ReplaceWithValue_ControlUse) {
+  CommonOperatorBuilder common(zone());
+  Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
+  Node* node = Node::New(zone(), 0, &kMockOpControl, 1, &start, false);
+  Node* success = Node::New(zone(), 0, common.IfSuccess(), 1, &node, false);
+  Node* use_control = Node::New(zone(), 0, common.Merge(1), 1, &success, false);
+  Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
+  NodeProperties::ReplaceWithValue(node, replacement);
+  EXPECT_EQ(start, use_control->InputAt(0));
+  EXPECT_EQ(0, node->UseCount());
+  EXPECT_EQ(2, start->UseCount());
+  EXPECT_EQ(0, replacement->UseCount());
+  EXPECT_THAT(start->uses(), ElementsAre(node, use_control));
+}
+
+
 TEST_F(NodePropertiesTest, FindProjection) {
   CommonOperatorBuilder common(zone());
   Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
index 70fd4d5..bc82535 100644 (file)
@@ -73,8 +73,10 @@ typedef TestWithZone ScheduleTest;
 
 namespace {
 
+const Operator kCallOperator(IrOpcode::kCall, Operator::kNoProperties,
+                             "MockCall", 0, 0, 0, 0, 0, 0);
 const Operator kBranchOperator(IrOpcode::kBranch, Operator::kNoProperties,
-                               "Branch", 0, 0, 0, 0, 0, 0);
+                               "MockBranch", 0, 0, 0, 0, 0, 0);
 const Operator kDummyOperator(IrOpcode::kParameter, Operator::kNoProperties,
                               "Dummy", 0, 0, 0, 0, 0, 0);
 
@@ -135,6 +137,35 @@ TEST_F(ScheduleTest, AddGoto) {
 }
 
 
+TEST_F(ScheduleTest, AddCall) {
+  Schedule schedule(zone());
+  BasicBlock* start = schedule.start();
+
+  Node* call = Node::New(zone(), 0, &kCallOperator, 0, nullptr, false);
+  BasicBlock* sblock = schedule.NewBasicBlock();
+  BasicBlock* eblock = schedule.NewBasicBlock();
+  schedule.AddCall(start, call, sblock, eblock);
+
+  EXPECT_EQ(start, schedule.block(call));
+
+  EXPECT_EQ(0u, start->PredecessorCount());
+  EXPECT_EQ(2u, start->SuccessorCount());
+  EXPECT_EQ(sblock, start->SuccessorAt(0));
+  EXPECT_EQ(eblock, start->SuccessorAt(1));
+  EXPECT_THAT(start->successors(), ElementsAre(sblock, eblock));
+
+  EXPECT_EQ(1u, sblock->PredecessorCount());
+  EXPECT_EQ(0u, sblock->SuccessorCount());
+  EXPECT_EQ(start, sblock->PredecessorAt(0));
+  EXPECT_THAT(sblock->predecessors(), ElementsAre(start));
+
+  EXPECT_EQ(1u, eblock->PredecessorCount());
+  EXPECT_EQ(0u, eblock->SuccessorCount());
+  EXPECT_EQ(start, eblock->PredecessorAt(0));
+  EXPECT_THAT(eblock->predecessors(), ElementsAre(start));
+}
+
+
 TEST_F(ScheduleTest, AddBranch) {
   Schedule schedule(zone());
   BasicBlock* start = schedule.start();