Chromium Code Reviews| Index: test/compiler-unittests/instruction-selector-unittest.cc |
| diff --git a/test/compiler-unittests/instruction-selector-unittest.cc b/test/compiler-unittests/instruction-selector-unittest.cc |
| index 1cf46f8728717d01f73d615ac72dfc930529a1d6..0f2859d2cc3455d9cf09f9ed5fe232dbdafe2508 100644 |
| --- a/test/compiler-unittests/instruction-selector-unittest.cc |
| +++ b/test/compiler-unittests/instruction-selector-unittest.cc |
| @@ -339,8 +339,8 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) { |
| Node* locals = m.NewNode(m.common()->StateValues(0)); |
| Node* stack = m.NewNode(m.common()->StateValues(0)); |
| - Node* state_node = |
| - m.NewNode(m.common()->FrameState(bailout_id), parameters, locals, stack); |
| + Node* state_node = m.NewNode(m.common()->FrameState(bailout_id), parameters, |
| + locals, stack, m.UndefinedConstant()); |
| m.Deoptimize(state_node); |
| Stream s = m.Build(kAllExceptNopInstructions); |
| @@ -376,8 +376,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) { |
| Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(43)); |
| Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44)); |
| Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45)); |
| - Node* frame_state_before = m.NewNode( |
| - m.common()->FrameState(bailout_id_before), parameters, locals, stack); |
| + Node* frame_state_before = |
| + m.NewNode(m.common()->FrameState(bailout_id_before), parameters, locals, |
| + stack, m.UndefinedConstant()); |
| StreamBuilder::Label deopt, cont; |
| // Build the call. |
| @@ -397,8 +398,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) { |
| Node* stack_after = |
| m.NewNode(m.common()->StateValues(2), m.Int32Constant(55), call); |
| - Node* frame_state_after = m.NewNode(m.common()->FrameState(bailout_id_after), |
| - parameters, locals, stack_after); |
| + Node* frame_state_after = |
| + m.NewNode(m.common()->FrameState(bailout_id_after), parameters, locals, |
| + stack_after, m.UndefinedConstant()); |
| m.Deoptimize(frame_state_after); |
| Stream s = m.Build(kAllExceptNopInstructions); |
| @@ -466,6 +468,133 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) { |
| EXPECT_EQ(index, s.size()); |
| } |
| + |
| +TARGET_TEST_F(InstructionSelectorTest, |
| + CallFunctionStubDeoptRecursiveFrameState) { |
| + StreamBuilder m(this, kMachAnyTagged, kMachAnyTagged, kMachAnyTagged, |
| + kMachAnyTagged); |
| + |
| + BailoutId bailout_id_before(42); |
| + BailoutId bailout_id_after(54); |
| + BailoutId bailout_id_parent(62); |
| + |
| + // Some arguments for the call node. |
| + Node* function_node = m.Parameter(0); |
| + Node* receiver = m.Parameter(1); |
| + Node* context = m.Int32Constant(1); // Context is ignored. |
| + |
| + // Build frame state for the state before the call. |
| + Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(63)); |
| + Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(64)); |
| + Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(65)); |
| + Node* frame_state_parent = |
| + m.NewNode(m.common()->FrameState(bailout_id_parent), parameters, locals, |
| + stack, m.UndefinedConstant()); |
| + |
| + Node* parameters2 = |
| + m.NewNode(m.common()->StateValues(1), m.Int32Constant(43)); |
| + Node* locals2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44)); |
| + Node* stack2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45)); |
| + Node* frame_state_before = |
| + m.NewNode(m.common()->FrameState(bailout_id_before), parameters2, locals2, |
| + stack2, frame_state_parent); |
| + |
| + StreamBuilder::Label deopt, cont; |
| + // Build the call. |
| + Node* call = |
| + m.CallFunctionStub0(function_node, receiver, context, frame_state_before, |
| + &cont, &deopt, CALL_AS_METHOD); |
| + |
| + // Create the continuation branch. |
| + m.Bind(&cont); |
| + m.NewNode(m.common()->Continuation(), call); |
| + m.Return(call); |
| + |
| + // Create the lazy deoptimization block (with a different frame state). |
| + m.Bind(&deopt); |
| + m.NewNode(m.common()->LazyDeoptimization(), call); |
| + |
| + Node* parameters3 = |
| + m.NewNode(m.common()->StateValues(1), m.Int32Constant(53)); |
| + Node* locals3 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(54)); |
| + Node* stack_after = |
| + m.NewNode(m.common()->StateValues(2), m.Int32Constant(55), call); |
| + |
| + Node* frame_state_after = |
| + m.NewNode(m.common()->FrameState(bailout_id_after), parameters3, locals3, |
| + stack_after, m.UndefinedConstant()); |
| + m.Deoptimize(frame_state_after); |
| + |
| + Stream s = m.Build(kAllExceptNopInstructions); |
| + |
| + // Skip until kArchCallJSFunction. |
| + size_t index = 0; |
| + for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject; |
| + index++) { |
| + } |
| + // Now we should have three instructions: call, return and deoptimize. |
| + ASSERT_EQ(index + 3, s.size()); |
| + |
| + // Check the call instruction |
| + const Instruction* call_instr = s[index++]; |
| + EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode()); |
| + size_t num_operands = |
| + 1 + // Code object. |
| + 1 + // Frame state deopt id |
| + 3 + // One input for each value in frame state. |
| + 3 + // One input for each value in the parent frame state. |
| + 1 + // Function. |
| + 1 + // Context. |
| + 2; // Continuation and deoptimization block labels. |
| + ASSERT_EQ(num_operands, call_instr->InputCount()); |
| + |
| + // Code object. |
| + EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate()); |
| + |
| + // Deoptimization id. |
| + int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1)); |
| + FrameStateDescriptor* desc_before = s.GetDeoptimizationEntry(deopt_id_before); |
| + EXPECT_EQ(bailout_id_before, desc_before->bailout_id()); |
| + EXPECT_EQ(1, desc_before->parameters_count()); |
| + EXPECT_EQ(1, desc_before->locals_count()); |
| + EXPECT_EQ(1, desc_before->stack_count()); |
| + EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(2))); |
|
Jarin
2014/08/29 15:07:07
Hmm, could we put the parent values first here?
sigurds
2014/09/01 08:48:18
Done.
|
| + EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(3))); |
| + EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(4))); |
| + // Values from parent environment should follow. |
| + EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(5))); |
| + EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(6))); |
| + EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(7))); |
| + |
| + // Function. |
| + EXPECT_EQ(function_node->id(), s.ToVreg(call_instr->InputAt(8))); |
| + // Context. |
| + EXPECT_EQ(context->id(), s.ToVreg(call_instr->InputAt(9))); |
| + // Continuation. |
| + EXPECT_EQ(cont.block()->id(), s.ToInt32(call_instr->InputAt(10))); |
| + // Deoptimization. |
| + EXPECT_EQ(deopt.block()->id(), s.ToInt32(call_instr->InputAt(11))); |
| + |
| + EXPECT_EQ(kArchRet, s[index++]->arch_opcode()); |
| + |
| + // Check the deoptimize instruction. |
| + const Instruction* deopt_instr = s[index++]; |
| + EXPECT_EQ(kArchDeoptimize, deopt_instr->arch_opcode()); |
| + ASSERT_EQ(5U, deopt_instr->InputCount()); |
| + int32_t deopt_id_after = s.ToInt32(deopt_instr->InputAt(0)); |
| + FrameStateDescriptor* desc_after = s.GetDeoptimizationEntry(deopt_id_after); |
| + EXPECT_EQ(bailout_id_after, desc_after->bailout_id()); |
| + EXPECT_EQ(1, desc_after->parameters_count()); |
| + EXPECT_EQ(1, desc_after->locals_count()); |
| + EXPECT_EQ(2, desc_after->stack_count()); |
| + // Parameter value from the frame state. |
| + EXPECT_EQ(53, s.ToInt32(deopt_instr->InputAt(1))); |
| + EXPECT_EQ(54, s.ToInt32(deopt_instr->InputAt(2))); |
| + EXPECT_EQ(55, s.ToInt32(deopt_instr->InputAt(3))); |
| + EXPECT_EQ(call->id(), s.ToVreg(deopt_instr->InputAt(4))); |
| + EXPECT_EQ(index, s.size()); |
| +} |
| + |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |