[turbofan] First shot at eager deoptimization in Turbofan.
authorjarin <jarin@chromium.org>
Wed, 4 Mar 2015 15:21:14 +0000 (07:21 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 4 Mar 2015 15:21:21 +0000 (15:21 +0000)
BUG=

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

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

25 files changed:
src/compiler/arm/code-generator-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/code-generator.h
src/compiler/common-operator.cc
src/compiler/common-operator.h
src/compiler/ia32/code-generator-ia32.cc
src/compiler/instruction-codes.h
src/compiler/instruction-selector.cc
src/compiler/instruction-selector.h
src/compiler/js-intrinsic-lowering.cc
src/compiler/js-intrinsic-lowering.h
src/compiler/mips/code-generator-mips.cc
src/compiler/mips64/code-generator-mips64.cc
src/compiler/node-properties.cc
src/compiler/node-properties.h
src/compiler/opcodes.h
src/compiler/ppc/code-generator-ppc.cc
src/compiler/schedule.cc
src/compiler/schedule.h
src/compiler/scheduler.cc
src/compiler/verifier.cc
src/compiler/x64/code-generator-x64.cc
src/runtime/runtime-test.cc
src/runtime/runtime.h
test/mjsunit/compiler/eager-deopt-simple.js [new file with mode: 0644]

index c2c9ea698e997ee251eca650bffcbb2b274b4266..f47eafebf51a1cc84ae9fa5cf29079330c99b3ac 100644 (file)
@@ -348,6 +348,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       // don't emit code for nops.
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       DCHECK_EQ(LeaveCC, i.OutputSBit());
@@ -785,9 +791,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index 378a421fa10593a94f7d44d3143814b1c04141ac..b3c7230807935956e4ad64ef3a763a9d94180d78 100644 (file)
@@ -368,6 +368,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchNop:
       // don't emit code for nops.
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       break;
@@ -892,9 +898,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index b957f0d7e8cbd06d6d123428b010cabb556dc85e..462b683cbe706ab3e7195df933ba3b006250217b 100644 (file)
@@ -74,7 +74,8 @@ class CodeGenerator FINAL : public GapResolver::Assembler {
   void AssembleArchLookupSwitch(Instruction* instr);
   void AssembleArchTableSwitch(Instruction* instr);
 
-  void AssembleDeoptimizerCall(int deoptimization_id);
+  void AssembleDeoptimizerCall(int deoptimization_id,
+                               Deoptimizer::BailoutType bailout_type);
 
   // Generates an architecture-specific, descriptor-specific prologue
   // to set up a stack frame.
index 5641db1bc2027dad79b565172b6b5033461769ba..aba43fa1bc667378f27e83e9e017be5821b20ffe 100644 (file)
@@ -119,6 +119,7 @@ size_t ProjectionIndexOf(const Operator* const op) {
   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(Deoptimize, Operator::kNoThrow, 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) \
   V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1)
index e74c62905f8d989f0d8cad0970b9875639012587..aab6a193e4e81e7cc7a2a64199fcc168e171940c 100644 (file)
@@ -178,6 +178,7 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
   const Operator* IfValue(int32_t value);
   const Operator* IfDefault();
   const Operator* Throw();
+  const Operator* Deoptimize();
   const Operator* Return();
 
   const Operator* Start(int num_formal_parameters);
index 3cb3d40539ca7a9e6e1a43d6275d86f6397a4277..d86151e616ca740ed286327bcf82ba8c613042b1 100644 (file)
@@ -321,6 +321,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchNop:
       // don't emit code for nops.
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       break;
@@ -870,9 +876,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index ef1e942ed4707783da8d4cccb2aab423d8c242ba..50e04349ea817f1142022f96c03bb589fad8c1e9 100644 (file)
@@ -40,6 +40,7 @@ namespace compiler {
   V(ArchLookupSwitch)       \
   V(ArchTableSwitch)        \
   V(ArchNop)                \
+  V(ArchDeoptimize)         \
   V(ArchRet)                \
   V(ArchStackPointer)       \
   V(ArchTruncateDoubleToI)  \
index 52aa233df83419e9aac39dd7a1b8f1e6f057774e..bffe6f76949f47bbaab4f1cdabec14efc917810f 100644 (file)
@@ -545,11 +545,19 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
     }
     case BasicBlock::kReturn: {
       // If the result itself is a return, return its input.
-      Node* value = (input != NULL && input->opcode() == IrOpcode::kReturn)
+      Node* value = (input != nullptr && input->opcode() == IrOpcode::kReturn)
                         ? input->InputAt(0)
                         : input;
       return VisitReturn(value);
     }
+    case BasicBlock::kDeoptimize: {
+      // If the result itself is a return, return its input.
+      Node* value =
+          (input != nullptr && input->opcode() == IrOpcode::kDeoptimize)
+              ? input->InputAt(0)
+              : input;
+      return VisitDeoptimize(value);
+    }
     case BasicBlock::kThrow:
       DCHECK_EQ(IrOpcode::kThrow, input->opcode());
       return VisitThrow(input->InputAt(0));
@@ -1090,6 +1098,29 @@ void InstructionSelector::VisitReturn(Node* value) {
 }
 
 
+void InstructionSelector::VisitDeoptimize(Node* value) {
+  DCHECK(FLAG_turbo_deoptimization);
+
+  OperandGenerator g(this);
+
+  FrameStateDescriptor* desc = GetFrameStateDescriptor(value);
+  size_t arg_count = desc->GetTotalSize() + 1;  // Include deopt id.
+
+  InstructionOperandVector args(instruction_zone());
+  args.reserve(arg_count);
+
+  InstructionSequence::StateId state_id =
+      sequence()->AddFrameStateDescriptor(desc);
+  args.push_back(g.TempImmediate(state_id.ToInt()));
+
+  AddFrameStateInputs(value, &args, desc);
+
+  DCHECK_EQ(args.size(), arg_count);
+
+  Emit(kArchDeoptimize, 0, nullptr, arg_count, &args.front(), 0, nullptr);
+}
+
+
 void InstructionSelector::VisitThrow(Node* value) {
   OperandGenerator g(this);
   Emit(kArchNop, g.NoOutput());  // TODO(titzer)
index f0d58f1fc6913cfba25cec1ecbc21dd505e64762..59a00d52e5dd4f88c6bd2b37f4a93d329da61f62 100644 (file)
@@ -205,9 +205,9 @@ class InstructionSelector FINAL {
   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 VisitDeoptimize(Node* value);
   void VisitReturn(Node* value);
   void VisitThrow(Node* value);
-  void VisitDeoptimize(Node* deopt);
 
   // ===========================================================================
 
index bbddc992e3c62de14e7664ee8904210c1c6cef63..87e74b73d95f619369456c62a42fd86d08eccb27 100644 (file)
@@ -20,8 +20,9 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
   if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange();
   const Runtime::Function* const f =
       Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id());
-  if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange();
   switch (f->function_id) {
+    case Runtime::kDeoptimizeNow:
+      return ReduceDeoptimizeNow(node);
     case Runtime::kInlineIsSmi:
       return ReduceInlineIsSmi(node);
     case Runtime::kInlineIsNonNegativeSmi:
@@ -41,6 +42,46 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
 }
 
 
+Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
+  if (!FLAG_turbo_deoptimization) return NoChange();
+
+  Node* frame_state = NodeProperties::GetFrameStateInput(node);
+  DCHECK_EQ(frame_state->opcode(), IrOpcode::kFrameState);
+
+  Node* effect = NodeProperties::GetEffectInput(node);
+  Node* control = NodeProperties::GetControlInput(node);
+
+  // We are making the continuation after the call dead. To
+  // model this, we generate if (true) statement with deopt
+  // in the true branch and continuation in the false branch.
+  Node* branch =
+      graph()->NewNode(common()->Branch(), jsgraph()->TrueConstant(), control);
+
+  // False branch - the original continuation.
+  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+  NodeProperties::ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect,
+                                   if_false);
+
+  // True branch: deopt.
+  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+  Node* deopt =
+      graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_true);
+
+  // Connect the deopt to the merge exiting the graph.
+  Node* end_pred = NodeProperties::GetControlInput(graph()->end());
+  if (end_pred->opcode() == IrOpcode::kMerge) {
+    int inputs = end_pred->op()->ControlInputCount() + 1;
+    end_pred->AppendInput(graph()->zone(), deopt);
+    end_pred->set_op(common()->Merge(inputs));
+  } else {
+    Node* merge = graph()->NewNode(common()->Merge(2), end_pred, deopt);
+    NodeProperties::ReplaceControlInput(graph()->end(), merge);
+  }
+
+  return Changed(deopt);
+}
+
+
 Reduction JSIntrinsicLowering::ReduceInlineIsSmi(Node* node) {
   return Change(node, simplified()->ObjectIsSmi());
 }
index bc188caa0680015064c02812edb27b411facfa69..d19c0f612123b297f937f27d089f58c0420b600e 100644 (file)
@@ -27,6 +27,7 @@ class JSIntrinsicLowering FINAL : public Reducer {
   Reduction Reduce(Node* node) FINAL;
 
  private:
+  Reduction ReduceDeoptimizeNow(Node* node);
   Reduction ReduceInlineIsSmi(Node* node);
   Reduction ReduceInlineIsNonNegativeSmi(Node* node);
   Reduction ReduceInlineIsInstanceType(Node* node, InstanceType instance_type);
index 32d9920a2a327efba88f336992da1876c1fafe6a..997f3fe591708c29e7f9a5d342e8ff9a2b65d019 100644 (file)
@@ -437,6 +437,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchNop:
       // don't emit code for nops.
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       break;
@@ -917,9 +923,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index 36308102b4a90a9535fe2d5682e30aae4c22b504..190001228c091c620fd501dc63800ff9745efeb3 100644 (file)
@@ -437,6 +437,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchNop:
       // don't emit code for nops.
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       break;
@@ -1077,9 +1083,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index 44e144883c322e8985cd3bbd3b44c6dc8a84426f..67ea06ceba52be81f33a7d1788e1bc687f5ecc55 100644 (file)
@@ -151,16 +151,20 @@ void NodeProperties::RemoveNonValueInputs(Node* node) {
 
 
 // static
-void NodeProperties::ReplaceWithValue(Node* node, Node* value, Node* effect) {
+void NodeProperties::ReplaceWithValue(Node* node, Node* value, Node* effect,
+                                      Node* control) {
   if (!effect && node->op()->EffectInputCount() > 0) {
     effect = NodeProperties::GetEffectInput(node);
   }
+  if (control == nullptr && node->op()->ControlInputCount() > 0) {
+    control = NodeProperties::GetControlInput(node);
+  }
 
   // Requires distinguishing between value, effect and control edges.
   for (Edge edge : node->use_edges()) {
     if (IsControlEdge(edge)) {
       DCHECK_EQ(IrOpcode::kIfSuccess, edge.from()->opcode());
-      Node* control = NodeProperties::GetControlInput(node);
+      DCHECK_NOT_NULL(control);
       edge.from()->ReplaceUses(control);
       edge.UpdateTo(NULL);
     } else if (IsEffectEdge(edge)) {
index 47396fb9a9386f23f87fb5c28f46420421521255..99dd5366e36b8f4a9f01ce6a12351dcb36487e14 100644 (file)
@@ -83,8 +83,8 @@ class NodeProperties FINAL {
   // Replace value uses of {node} with {value} and effect uses of {node} with
   // {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);
-
+  static void ReplaceWithValue(Node* node, Node* value, Node* effect = nullptr,
+                               Node* control = nullptr);
 
   // ---------------------------------------------------------------------------
   // Miscellaneous utilities.
index 18f40dd6a76b38b8f4e4c10b8363e0ed5ff6f328..6e9e1fb8f90add1de5261fdc0e57b134d4c7d954 100644 (file)
@@ -18,6 +18,7 @@
   V(IfValue)                     \
   V(IfDefault)                   \
   V(Merge)                       \
+  V(Deoptimize)                  \
   V(Return)                      \
   V(OsrNormalEntry)              \
   V(OsrLoopEntry)                \
index 2e1f48f325b19b977497ab77b14f2a9f42ea5941..d1889fb426ec002a06a65ca52bf0a4dc0bc8f3c8 100644 (file)
@@ -572,6 +572,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       // don't emit code for nops.
       DCHECK_EQ(LeaveRC, i.OutputRCBit());
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       DCHECK_EQ(LeaveRC, i.OutputRCBit());
@@ -1116,9 +1122,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index c3ee33a446802c12fd6503a1fb9d696857daf773..f30e5f61ceb9d944a8afdbe0c16ba2a5822f4a62 100644 (file)
@@ -106,6 +106,8 @@ std::ostream& operator<<(std::ostream& os, const BasicBlock::Control& c) {
       return os << "branch";
     case BasicBlock::kSwitch:
       return os << "switch";
+    case BasicBlock::kDeoptimize:
+      return os << "deoptimize";
     case BasicBlock::kReturn:
       return os << "return";
     case BasicBlock::kThrow:
@@ -239,6 +241,14 @@ void Schedule::AddReturn(BasicBlock* block, Node* input) {
 }
 
 
+void Schedule::AddDeoptimize(BasicBlock* block, Node* input) {
+  DCHECK_EQ(BasicBlock::kNone, block->control());
+  block->set_control(BasicBlock::kDeoptimize);
+  SetControlInput(block, input);
+  if (block != end()) AddSuccessor(block, end());
+}
+
+
 void Schedule::AddThrow(BasicBlock* block, Node* input) {
   DCHECK_EQ(BasicBlock::kNone, block->control());
   block->set_control(BasicBlock::kThrow);
index 5a5a22beb24fb43f33143db995aa5257a01647df..d940e541db8f747abbe7cc86c9fdc61bbbfa1690 100644 (file)
@@ -30,13 +30,15 @@ class BasicBlock FINAL : public ZoneObject {
  public:
   // Possible control nodes that can end a block.
   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.
-    kThrow    // Throw an exception.
+    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.
+    kDeoptimize,  // Return a value from this method.
+    kReturn,      // Return a value from this method.
+    kThrow        // Throw an exception.
   };
 
   class Id {
@@ -215,6 +217,9 @@ class Schedule FINAL : public ZoneObject {
   void AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks,
                  size_t succ_count);
 
+  // BasicBlock building: add a deoptimize at the end of {block}.
+  void AddDeoptimize(BasicBlock* block, Node* input);
+
   // BasicBlock building: add a return at the end of {block}.
   void AddReturn(BasicBlock* block, Node* input);
 
index 9a4c7af6eade6523c345fff23684c0f204168fb5..a0ab947eab6f7fdf2db7aec95473f05e3daa9445 100644 (file)
@@ -345,6 +345,10 @@ class CFGBuilder : public ZoneObject {
         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
         ConnectSwitch(node);
         break;
+      case IrOpcode::kDeoptimize:
+        scheduler_->UpdatePlacement(node, Scheduler::kFixed);
+        ConnectDeoptimize(node);
+        break;
       case IrOpcode::kReturn:
         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
         ConnectReturn(node);
@@ -494,6 +498,13 @@ class CFGBuilder : public ZoneObject {
     schedule_->AddReturn(return_block, ret);
   }
 
+  void ConnectDeoptimize(Node* deopt) {
+    Node* deoptimize_control = NodeProperties::GetControlInput(deopt);
+    BasicBlock* deoptimize_block = FindPredecessorBlock(deoptimize_control);
+    TraceConnect(deopt, deoptimize_block, NULL);
+    schedule_->AddDeoptimize(deoptimize_block, deopt);
+  }
+
   void ConnectThrow(Node* thr) {
     Node* throw_control = NodeProperties::GetControlInput(thr);
     BasicBlock* throw_block = FindPredecessorBlock(throw_control);
index ffba4cac6063a5d1fe07ff145d09080225aaf610..b502621d506974ac04f5f8bb434ca1322e457260 100644 (file)
@@ -282,6 +282,10 @@ void Verifier::Visitor::Check(Node* node) {
       // Type is empty.
       CheckNotTyped(node);
       break;
+    case IrOpcode::kDeoptimize:
+      // TODO(rossberg): check successor is End
+      // Type is empty.
+      CheckNotTyped(node);
     case IrOpcode::kReturn:
       // TODO(rossberg): check successor is End
       // Type is empty.
index 921bf207ae02969cdd310539aa6934dc0d5c6809..8bb0d6c936da82e9ed108dba0ab264dfa1b7eab9 100644 (file)
@@ -555,6 +555,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArchNop:
       // don't emit code for nops.
       break;
+    case kArchDeoptimize: {
+      int deopt_state_id =
+          BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
+      AssembleDeoptimizerCall(deopt_state_id, Deoptimizer::EAGER);
+      break;
+    }
     case kArchRet:
       AssembleReturn();
       break;
@@ -1165,9 +1171,10 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
 }
 
 
-void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
+void CodeGenerator::AssembleDeoptimizerCall(
+    int deoptimization_id, Deoptimizer::BailoutType bailout_type) {
   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
-      isolate(), deoptimization_id, Deoptimizer::LAZY);
+      isolate(), deoptimization_id, bailout_type);
   __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
 }
 
index 89e1f2a69662fee8051345cdcc86e6047990c145..7bf74cca57a8eaf983c8d079d97a9610e2e1a04e 100644 (file)
@@ -30,6 +30,36 @@ RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
 }
 
 
+RUNTIME_FUNCTION(Runtime_DeoptimizeNow) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 0);
+
+  Handle<JSFunction> function;
+
+  // If the argument is 'undefined', deoptimize the topmost
+  // function.
+  JavaScriptFrameIterator it(isolate);
+  while (!it.done()) {
+    if (it.frame()->is_java_script()) {
+      function = Handle<JSFunction>(it.frame()->function());
+      break;
+    }
+  }
+  if (function.is_null()) return isolate->heap()->undefined_value();
+
+  if (!function->IsOptimized()) return isolate->heap()->undefined_value();
+
+  // TODO(turbofan): Deoptimization is not supported yet.
+  if (function->code()->is_turbofanned() && !FLAG_turbo_deoptimization) {
+    return isolate->heap()->undefined_value();
+  }
+
+  Deoptimizer::DeoptimizeFunction(*function);
+
+  return isolate->heap()->undefined_value();
+}
+
+
 RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
   SealHandleScope shs(isolate);
   DCHECK(args.length() == 0);
index f173cc96febdd645feda979fd01879446192e58b..c6eed052a45d3f68bda8914e4f3f7a423838de0f 100644 (file)
@@ -62,6 +62,7 @@ namespace internal {
   F(GetFunctionDelegate, 1, 1)                 \
   F(GetConstructorDelegate, 1, 1)              \
   F(DeoptimizeFunction, 1, 1)                  \
+  F(DeoptimizeNow, 0, 1)                       \
   F(ClearFunctionTypeFeedback, 1, 1)           \
   F(RunningInSimulator, 0, 1)                  \
   F(IsConcurrentRecompilationSupported, 0, 1)  \
diff --git a/test/mjsunit/compiler/eager-deopt-simple.js b/test/mjsunit/compiler/eager-deopt-simple.js
new file mode 100644 (file)
index 0000000..72ca317
--- /dev/null
@@ -0,0 +1,18 @@
+// 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.
+
+// Flags: --allow-natives-syntax
+
+function g(a, b, c) {
+  return a + b + c;
+}
+
+function f() {
+  return g(1, (%DeoptimizeNow(), 2), 3);
+}
+
+f();
+f();
+%OptimizeFunctionOnNextCall(f);
+assertEquals(6, f());