Unit test of instruction selection for calls with deoptimization.
BUG= R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/505133002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23467 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
71fbe7d4ec
commit
8eb5c1524d
@ -166,8 +166,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kArchDeoptimize: {
|
case kArchDeoptimize: {
|
||||||
int deoptimization_id = MiscField::decode(instr->opcode());
|
int deoptimization_id = BuildTranslation(instr, 0);
|
||||||
BuildTranslation(instr, 0, deoptimization_id);
|
|
||||||
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
||||||
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
||||||
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
||||||
|
@ -160,8 +160,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kArchDeoptimize: {
|
case kArchDeoptimize: {
|
||||||
int deoptimization_id = MiscField::decode(instr->opcode());
|
int deoptimization_id = BuildTranslation(instr, 0);
|
||||||
BuildTranslation(instr, 0, deoptimization_id);
|
|
||||||
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
||||||
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
||||||
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
||||||
|
@ -258,10 +258,9 @@ void CodeGenerator::AddSafepointAndDeopt(Instruction* instr) {
|
|||||||
// If the frame state is present, it starts at argument 1
|
// If the frame state is present, it starts at argument 1
|
||||||
// (just after the code address).
|
// (just after the code address).
|
||||||
InstructionOperandConverter converter(this, instr);
|
InstructionOperandConverter converter(this, instr);
|
||||||
// Argument 1 is deoptimization id.
|
// Deoptimization info starts at argument 1
|
||||||
int deoptimization_id = converter.ToConstant(instr->InputAt(1)).ToInt32();
|
int frame_state_offset = 1;
|
||||||
// The actual frame state values start with argument 2.
|
int deoptimization_id = BuildTranslation(instr, frame_state_offset);
|
||||||
int first_state_value_offset = 2;
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Make sure all the values live in stack slots or they are immediates.
|
// Make sure all the values live in stack slots or they are immediates.
|
||||||
// (The values should not live in register because registers are clobbered
|
// (The values should not live in register because registers are clobbered
|
||||||
@ -269,11 +268,10 @@ void CodeGenerator::AddSafepointAndDeopt(Instruction* instr) {
|
|||||||
FrameStateDescriptor* descriptor =
|
FrameStateDescriptor* descriptor =
|
||||||
code()->GetDeoptimizationEntry(deoptimization_id);
|
code()->GetDeoptimizationEntry(deoptimization_id);
|
||||||
for (int i = 0; i < descriptor->size(); i++) {
|
for (int i = 0; i < descriptor->size(); i++) {
|
||||||
InstructionOperand* op = instr->InputAt(first_state_value_offset + i);
|
InstructionOperand* op = instr->InputAt(frame_state_offset + 1 + i);
|
||||||
CHECK(op->IsStackSlot() || op->IsImmediate());
|
CHECK(op->IsStackSlot() || op->IsImmediate());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
BuildTranslation(instr, first_state_value_offset, deoptimization_id);
|
|
||||||
safepoints()->RecordLazyDeoptimizationIndex(deoptimization_id);
|
safepoints()->RecordLazyDeoptimizationIndex(deoptimization_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,9 +308,12 @@ int CodeGenerator::DefineDeoptimizationLiteral(Handle<Object> literal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CodeGenerator::BuildTranslation(Instruction* instr,
|
int CodeGenerator::BuildTranslation(Instruction* instr,
|
||||||
int first_argument_index,
|
int frame_state_offset) {
|
||||||
int deoptimization_id) {
|
InstructionOperandConverter i(this, instr);
|
||||||
|
int deoptimization_id = i.InputInt32(frame_state_offset);
|
||||||
|
frame_state_offset++;
|
||||||
|
|
||||||
// We should build translation only once.
|
// We should build translation only once.
|
||||||
DCHECK_EQ(NULL, deoptimization_states_[deoptimization_id]);
|
DCHECK_EQ(NULL, deoptimization_states_[deoptimization_id]);
|
||||||
|
|
||||||
@ -325,11 +326,13 @@ void CodeGenerator::BuildTranslation(Instruction* instr,
|
|||||||
|
|
||||||
for (int i = 0; i < descriptor->size(); i++) {
|
for (int i = 0; i < descriptor->size(); i++) {
|
||||||
AddTranslationForOperand(&translation, instr,
|
AddTranslationForOperand(&translation, instr,
|
||||||
instr->InputAt(i + first_argument_index));
|
instr->InputAt(i + frame_state_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
deoptimization_states_[deoptimization_id] =
|
deoptimization_states_[deoptimization_id] =
|
||||||
new (zone()) DeoptimizationState(translation.index());
|
new (zone()) DeoptimizationState(translation.index());
|
||||||
|
|
||||||
|
return deoptimization_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,8 +87,7 @@ class CodeGenerator V8_FINAL : public GapResolver::Assembler {
|
|||||||
Safepoint::Id safepoint_id);
|
Safepoint::Id safepoint_id);
|
||||||
void PopulateDeoptimizationData(Handle<Code> code);
|
void PopulateDeoptimizationData(Handle<Code> code);
|
||||||
int DefineDeoptimizationLiteral(Handle<Object> literal);
|
int DefineDeoptimizationLiteral(Handle<Object> literal);
|
||||||
void BuildTranslation(Instruction* instr, int first_argument_index,
|
int BuildTranslation(Instruction* instr, int frame_state_offset);
|
||||||
int deoptimization_id);
|
|
||||||
void AddTranslationForOperand(Translation* translation, Instruction* instr,
|
void AddTranslationForOperand(Translation* translation, Instruction* instr,
|
||||||
InstructionOperand* op);
|
InstructionOperand* op);
|
||||||
void AddNopForSmiCodeInlining();
|
void AddNopForSmiCodeInlining();
|
||||||
|
@ -141,8 +141,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kArchDeoptimize: {
|
case kArchDeoptimize: {
|
||||||
int deoptimization_id = MiscField::decode(instr->opcode());
|
int deoptimization_id = BuildTranslation(instr, 0);
|
||||||
BuildTranslation(instr, 0, deoptimization_id);
|
|
||||||
|
|
||||||
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
||||||
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
||||||
|
@ -1082,17 +1082,19 @@ void InstructionSelector::VisitDeoptimize(Node* deopt) {
|
|||||||
DCHECK(deopt->op()->opcode() == IrOpcode::kDeoptimize);
|
DCHECK(deopt->op()->opcode() == IrOpcode::kDeoptimize);
|
||||||
Node* state = deopt->InputAt(0);
|
Node* state = deopt->InputAt(0);
|
||||||
FrameStateDescriptor* descriptor = GetFrameStateDescriptor(state);
|
FrameStateDescriptor* descriptor = GetFrameStateDescriptor(state);
|
||||||
|
int deoptimization_id = sequence()->AddDeoptimizationEntry(descriptor);
|
||||||
|
|
||||||
InstructionOperandVector inputs(zone());
|
InstructionOperandVector inputs(zone());
|
||||||
inputs.reserve(descriptor->size());
|
inputs.reserve(descriptor->size() + 1);
|
||||||
|
|
||||||
|
OperandGenerator g(this);
|
||||||
|
inputs.push_back(g.TempImmediate(deoptimization_id));
|
||||||
|
|
||||||
AddFrameStateInputs(state, &inputs, descriptor);
|
AddFrameStateInputs(state, &inputs, descriptor);
|
||||||
|
|
||||||
DCHECK_EQ(descriptor->size(), inputs.size());
|
DCHECK_EQ(descriptor->size() + 1, inputs.size());
|
||||||
|
|
||||||
int deoptimization_id = sequence()->AddDeoptimizationEntry(descriptor);
|
Emit(kArchDeoptimize, 0, NULL, inputs.size(), &inputs.front(), 0, NULL);
|
||||||
Emit(kArchDeoptimize | MiscField::encode(deoptimization_id), 0, NULL,
|
|
||||||
inputs.size(), &inputs.front(), 0, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,6 +83,31 @@ void RawMachineAssembler::Deoptimize(Node* state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* RawMachineAssembler::CallFunctionStub0(Node* function, Node* receiver,
|
||||||
|
Node* context, Node* frame_state,
|
||||||
|
Label* continuation,
|
||||||
|
Label* deoptimization,
|
||||||
|
CallFunctionFlags flags) {
|
||||||
|
CallFunctionStub stub(isolate(), 0, flags);
|
||||||
|
CodeStubInterfaceDescriptor* d = isolate()->code_stub_interface_descriptor(
|
||||||
|
reinterpret_cast<CodeStub*>(&stub)->MajorKey());
|
||||||
|
stub.InitializeInterfaceDescriptor(d);
|
||||||
|
|
||||||
|
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||||
|
d, 1, static_cast<CallDescriptor::DeoptimizationSupport>(
|
||||||
|
CallDescriptor::kLazyDeoptimization |
|
||||||
|
CallDescriptor::kNeedsFrameState),
|
||||||
|
zone());
|
||||||
|
Node* stub_code = HeapConstant(stub.GetCode());
|
||||||
|
Node* call = graph()->NewNode(common()->Call(desc), stub_code, function,
|
||||||
|
receiver, context, frame_state);
|
||||||
|
schedule()->AddCall(CurrentBlock(), call, Use(continuation),
|
||||||
|
Use(deoptimization));
|
||||||
|
current_block_ = NULL;
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* RawMachineAssembler::CallJS0(Node* function, Node* receiver,
|
Node* RawMachineAssembler::CallJS0(Node* function, Node* receiver,
|
||||||
Label* continuation, Label* deoptimization) {
|
Label* continuation, Label* deoptimization) {
|
||||||
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(1, zone());
|
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(1, zone());
|
||||||
|
@ -71,6 +71,10 @@ class RawMachineAssembler : public GraphBuilder,
|
|||||||
Label* Exit();
|
Label* Exit();
|
||||||
void Goto(Label* label);
|
void Goto(Label* label);
|
||||||
void Branch(Node* condition, Label* true_val, Label* false_val);
|
void Branch(Node* condition, Label* true_val, Label* false_val);
|
||||||
|
// Call through CallFunctionStub with lazy deopt and frame-state.
|
||||||
|
Node* CallFunctionStub0(Node* function, Node* receiver, Node* context,
|
||||||
|
Node* frame_state, Label* continuation,
|
||||||
|
Label* deoptimization, CallFunctionFlags flags);
|
||||||
// Call to a JS function with zero parameters.
|
// Call to a JS function with zero parameters.
|
||||||
Node* CallJS0(Node* function, Node* receiver, Label* continuation,
|
Node* CallJS0(Node* function, Node* receiver, Label* continuation,
|
||||||
Label* deoptimization);
|
Label* deoptimization);
|
||||||
|
@ -235,8 +235,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kArchDeoptimize: {
|
case kArchDeoptimize: {
|
||||||
int deoptimization_id = MiscField::decode(instr->opcode());
|
int deoptimization_id = BuildTranslation(instr, 0);
|
||||||
BuildTranslation(instr, 0, deoptimization_id);
|
|
||||||
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
|
||||||
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
isolate(), deoptimization_id, Deoptimizer::LAZY);
|
||||||
__ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
__ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
|
||||||
|
@ -210,7 +210,6 @@ TEST(TurboTrivialDeoptCodegen) {
|
|||||||
|
|
||||||
// Check that we deoptimize to the right AST id.
|
// Check that we deoptimize to the right AST id.
|
||||||
CHECK_EQ(1, data->DeoptCount());
|
CHECK_EQ(1, data->DeoptCount());
|
||||||
CHECK_EQ(1, data->DeoptCount());
|
|
||||||
CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
|
CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1895,7 +1895,6 @@ TEST_F(InstructionSelectorTest, Word32AndWithWord32ShrWithImmediateForARMv7) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -57,6 +57,9 @@ InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mode == kAllExceptNopInstructions && instr->arch_opcode() == kArchNop) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (size_t i = 0; i < instr->OutputCount(); ++i) {
|
for (size_t i = 0; i < instr->OutputCount(); ++i) {
|
||||||
InstructionOperand* output = instr->OutputAt(i);
|
InstructionOperand* output = instr->OutputAt(i);
|
||||||
EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
|
EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
|
||||||
@ -94,6 +97,9 @@ InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
|
|||||||
s.references_.insert(virtual_register);
|
s.references_.insert(virtual_register);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < sequence.GetDeoptimizationEntryCount(); i++) {
|
||||||
|
s.deoptimization_entries_.push_back(sequence.GetDeoptimizationEntry(i));
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +313,159 @@ TARGET_TEST_F(InstructionSelectorTest, ValueEffect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Calls with deoptimization.
|
||||||
|
TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
|
||||||
|
StreamBuilder m(this, kMachAnyTagged, kMachAnyTagged, kMachAnyTagged);
|
||||||
|
|
||||||
|
BailoutId bailout_id(42);
|
||||||
|
|
||||||
|
Node* function_node = m.Parameter(0);
|
||||||
|
Node* receiver = m.Parameter(1);
|
||||||
|
StreamBuilder::Label deopt, cont;
|
||||||
|
|
||||||
|
// TODO(jarin) Add frame state.
|
||||||
|
Node* call = m.CallJS0(function_node, receiver, &cont, &deopt);
|
||||||
|
|
||||||
|
m.Bind(&cont);
|
||||||
|
m.NewNode(m.common()->Continuation(), call);
|
||||||
|
m.Return(call);
|
||||||
|
|
||||||
|
m.Bind(&deopt);
|
||||||
|
m.NewNode(m.common()->LazyDeoptimization(), call);
|
||||||
|
|
||||||
|
Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(1));
|
||||||
|
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);
|
||||||
|
m.Deoptimize(state_node);
|
||||||
|
|
||||||
|
Stream s = m.Build(kAllExceptNopInstructions);
|
||||||
|
|
||||||
|
// Skip until kArchCallJSFunction.
|
||||||
|
size_t index = 0;
|
||||||
|
for (; index < s.size() && s[index]->arch_opcode() != kArchCallJSFunction;
|
||||||
|
index++) {
|
||||||
|
}
|
||||||
|
// Now we should have three instructions: call, return and deoptimize.
|
||||||
|
ASSERT_EQ(index + 3, s.size());
|
||||||
|
|
||||||
|
EXPECT_EQ(kArchCallJSFunction, s[index++]->arch_opcode());
|
||||||
|
EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
|
||||||
|
EXPECT_EQ(kArchDeoptimize, s[index++]->arch_opcode());
|
||||||
|
EXPECT_EQ(index, s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) {
|
||||||
|
StreamBuilder m(this, kMachAnyTagged, kMachAnyTagged, kMachAnyTagged,
|
||||||
|
kMachAnyTagged);
|
||||||
|
|
||||||
|
BailoutId bailout_id_before(42);
|
||||||
|
BailoutId bailout_id_after(54);
|
||||||
|
|
||||||
|
// 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(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);
|
||||||
|
|
||||||
|
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* 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);
|
||||||
|
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 +
|
||||||
|
3 + // Frame state deopt id + one input for each value in 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)));
|
||||||
|
EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(3)));
|
||||||
|
EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(4)));
|
||||||
|
|
||||||
|
// Function.
|
||||||
|
EXPECT_EQ(function_node->id(), s.ToVreg(call_instr->InputAt(5)));
|
||||||
|
// Context.
|
||||||
|
EXPECT_EQ(context->id(), s.ToVreg(call_instr->InputAt(6)));
|
||||||
|
// Continuation.
|
||||||
|
EXPECT_EQ(cont.block()->id(), s.ToInt32(call_instr->InputAt(7)));
|
||||||
|
// Deoptimization.
|
||||||
|
EXPECT_EQ(deopt.block()->id(), s.ToInt32(call_instr->InputAt(8)));
|
||||||
|
|
||||||
|
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(43, s.ToInt32(deopt_instr->InputAt(1)));
|
||||||
|
EXPECT_EQ(44, 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 compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -26,7 +26,11 @@ class InstructionSelectorTest : public CompilerTest {
|
|||||||
|
|
||||||
class Stream;
|
class Stream;
|
||||||
|
|
||||||
enum StreamBuilderMode { kAllInstructions, kTargetInstructions };
|
enum StreamBuilderMode {
|
||||||
|
kAllInstructions,
|
||||||
|
kTargetInstructions,
|
||||||
|
kAllExceptNopInstructions
|
||||||
|
};
|
||||||
|
|
||||||
class StreamBuilder V8_FINAL : public RawMachineAssembler {
|
class StreamBuilder V8_FINAL : public RawMachineAssembler {
|
||||||
public:
|
public:
|
||||||
@ -146,6 +150,15 @@ class InstructionSelectorTest : public CompilerTest {
|
|||||||
return UnallocatedOperand::cast(operand)->virtual_register();
|
return UnallocatedOperand::cast(operand)->virtual_register();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameStateDescriptor* GetDeoptimizationEntry(int deoptimization_id) {
|
||||||
|
EXPECT_LT(deoptimization_id, GetDeoptimizationEntryCount());
|
||||||
|
return deoptimization_entries_[deoptimization_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetDeoptimizationEntryCount() {
|
||||||
|
return static_cast<int>(deoptimization_entries_.size());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Constant ToConstant(const InstructionOperand* operand) const {
|
Constant ToConstant(const InstructionOperand* operand) const {
|
||||||
ConstantMap::const_iterator i;
|
ConstantMap::const_iterator i;
|
||||||
@ -170,6 +183,7 @@ class InstructionSelectorTest : public CompilerTest {
|
|||||||
std::deque<Instruction*> instructions_;
|
std::deque<Instruction*> instructions_;
|
||||||
std::set<int> doubles_;
|
std::set<int> doubles_;
|
||||||
std::set<int> references_;
|
std::set<int> references_;
|
||||||
|
std::deque<FrameStateDescriptor*> deoptimization_entries_;
|
||||||
};
|
};
|
||||||
|
|
||||||
base::RandomNumberGenerator rng_;
|
base::RandomNumberGenerator rng_;
|
||||||
|
Loading…
Reference in New Issue
Block a user