From 8d2e45669f84076e95131503ea623f32483b3a7d Mon Sep 17 00:00:00 2001 From: jarin Date: Wed, 4 Mar 2015 07:21:14 -0800 Subject: [PATCH] [turbofan] First shot at eager deoptimization in Turbofan. BUG= Review URL: https://codereview.chromium.org/961973002 Cr-Commit-Position: refs/heads/master@{#26993} --- src/compiler/arm/code-generator-arm.cc | 11 ++++- src/compiler/arm64/code-generator-arm64.cc | 11 ++++- src/compiler/code-generator.h | 3 +- src/compiler/common-operator.cc | 1 + src/compiler/common-operator.h | 1 + src/compiler/ia32/code-generator-ia32.cc | 11 ++++- src/compiler/instruction-codes.h | 1 + src/compiler/instruction-selector.cc | 33 ++++++++++++++- src/compiler/instruction-selector.h | 2 +- src/compiler/js-intrinsic-lowering.cc | 43 +++++++++++++++++++- src/compiler/js-intrinsic-lowering.h | 1 + src/compiler/mips/code-generator-mips.cc | 11 ++++- src/compiler/mips64/code-generator-mips64.cc | 11 ++++- src/compiler/node-properties.cc | 8 +++- src/compiler/node-properties.h | 4 +- src/compiler/opcodes.h | 1 + src/compiler/ppc/code-generator-ppc.cc | 11 ++++- src/compiler/schedule.cc | 10 +++++ src/compiler/schedule.h | 19 +++++---- src/compiler/scheduler.cc | 11 +++++ src/compiler/verifier.cc | 4 ++ src/compiler/x64/code-generator-x64.cc | 11 ++++- src/runtime/runtime-test.cc | 30 ++++++++++++++ src/runtime/runtime.h | 1 + test/mjsunit/compiler/eager-deopt-simple.js | 18 ++++++++ 25 files changed, 239 insertions(+), 29 deletions(-) create mode 100644 test/mjsunit/compiler/eager-deopt-simple.js diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index c2c9ea698..f47eafebf 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -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); } diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index 378a421fa..b3c723080 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -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); } diff --git a/src/compiler/code-generator.h b/src/compiler/code-generator.h index b957f0d7e..462b683cb 100644 --- a/src/compiler/code-generator.h +++ b/src/compiler/code-generator.h @@ -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. diff --git a/src/compiler/common-operator.cc b/src/compiler/common-operator.cc index 5641db1bc..aba43fa1b 100644 --- a/src/compiler/common-operator.cc +++ b/src/compiler/common-operator.cc @@ -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) diff --git a/src/compiler/common-operator.h b/src/compiler/common-operator.h index e74c62905..aab6a193e 100644 --- a/src/compiler/common-operator.h +++ b/src/compiler/common-operator.h @@ -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); diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index 3cb3d4053..d86151e61 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -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); } diff --git a/src/compiler/instruction-codes.h b/src/compiler/instruction-codes.h index ef1e942ed..50e04349e 100644 --- a/src/compiler/instruction-codes.h +++ b/src/compiler/instruction-codes.h @@ -40,6 +40,7 @@ namespace compiler { V(ArchLookupSwitch) \ V(ArchTableSwitch) \ V(ArchNop) \ + V(ArchDeoptimize) \ V(ArchRet) \ V(ArchStackPointer) \ V(ArchTruncateDoubleToI) \ diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index 52aa233df..bffe6f769 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -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) diff --git a/src/compiler/instruction-selector.h b/src/compiler/instruction-selector.h index f0d58f1fc..59a00d52e 100644 --- a/src/compiler/instruction-selector.h +++ b/src/compiler/instruction-selector.h @@ -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); // =========================================================================== diff --git a/src/compiler/js-intrinsic-lowering.cc b/src/compiler/js-intrinsic-lowering.cc index bbddc992e..87e74b73d 100644 --- a/src/compiler/js-intrinsic-lowering.cc +++ b/src/compiler/js-intrinsic-lowering.cc @@ -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()); } diff --git a/src/compiler/js-intrinsic-lowering.h b/src/compiler/js-intrinsic-lowering.h index bc188caa0..d19c0f612 100644 --- a/src/compiler/js-intrinsic-lowering.h +++ b/src/compiler/js-intrinsic-lowering.h @@ -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); diff --git a/src/compiler/mips/code-generator-mips.cc b/src/compiler/mips/code-generator-mips.cc index 32d9920a2..997f3fe59 100644 --- a/src/compiler/mips/code-generator-mips.cc +++ b/src/compiler/mips/code-generator-mips.cc @@ -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); } diff --git a/src/compiler/mips64/code-generator-mips64.cc b/src/compiler/mips64/code-generator-mips64.cc index 36308102b..190001228 100644 --- a/src/compiler/mips64/code-generator-mips64.cc +++ b/src/compiler/mips64/code-generator-mips64.cc @@ -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); } diff --git a/src/compiler/node-properties.cc b/src/compiler/node-properties.cc index 44e144883..67ea06ceb 100644 --- a/src/compiler/node-properties.cc +++ b/src/compiler/node-properties.cc @@ -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)) { diff --git a/src/compiler/node-properties.h b/src/compiler/node-properties.h index 47396fb9a..99dd5366e 100644 --- a/src/compiler/node-properties.h +++ b/src/compiler/node-properties.h @@ -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. diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 18f40dd6a..6e9e1fb8f 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -18,6 +18,7 @@ V(IfValue) \ V(IfDefault) \ V(Merge) \ + V(Deoptimize) \ V(Return) \ V(OsrNormalEntry) \ V(OsrLoopEntry) \ diff --git a/src/compiler/ppc/code-generator-ppc.cc b/src/compiler/ppc/code-generator-ppc.cc index 2e1f48f32..d1889fb42 100644 --- a/src/compiler/ppc/code-generator-ppc.cc +++ b/src/compiler/ppc/code-generator-ppc.cc @@ -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); } diff --git a/src/compiler/schedule.cc b/src/compiler/schedule.cc index c3ee33a44..f30e5f61c 100644 --- a/src/compiler/schedule.cc +++ b/src/compiler/schedule.cc @@ -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); diff --git a/src/compiler/schedule.h b/src/compiler/schedule.h index 5a5a22beb..d940e541d 100644 --- a/src/compiler/schedule.h +++ b/src/compiler/schedule.h @@ -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); diff --git a/src/compiler/scheduler.cc b/src/compiler/scheduler.cc index 9a4c7af6e..a0ab947ea 100644 --- a/src/compiler/scheduler.cc +++ b/src/compiler/scheduler.cc @@ -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); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index ffba4cac6..b502621d5 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -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. diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index 921bf207a..8bb0d6c93 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -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); } diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc index 89e1f2a69..7bf74cca5 100644 --- a/src/runtime/runtime-test.cc +++ b/src/runtime/runtime-test.cc @@ -30,6 +30,36 @@ RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) { } +RUNTIME_FUNCTION(Runtime_DeoptimizeNow) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + + Handle function; + + // If the argument is 'undefined', deoptimize the topmost + // function. + JavaScriptFrameIterator it(isolate); + while (!it.done()) { + if (it.frame()->is_java_script()) { + function = Handle(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); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index f173cc96f..c6eed052a 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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 index 000000000..72ca317a5 --- /dev/null +++ b/test/mjsunit/compiler/eager-deopt-simple.js @@ -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()); -- 2.34.1