break;
}
case kArchDeoptimize: {
- int deoptimization_id = MiscField::decode(instr->opcode());
- BuildTranslation(instr, 0, deoptimization_id);
+ int deoptimization_id = BuildTranslation(instr, 0);
+
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
break;
}
case kArchDeoptimize: {
- int deoptimization_id = MiscField::decode(instr->opcode());
- BuildTranslation(instr, 0, deoptimization_id);
+ int deoptimization_id = BuildTranslation(instr, 0);
+
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
__ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
// If the frame state is present, it starts at argument 1
// (just after the code address).
InstructionOperandConverter converter(this, instr);
- // Argument 1 is deoptimization id.
- int deoptimization_id = converter.ToConstant(instr->InputAt(1)).ToInt32();
- // The actual frame state values start with argument 2.
- int first_state_value_offset = 2;
+ // Deoptimization info starts at argument 1
+ int frame_state_offset = 1;
+ int deoptimization_id = BuildTranslation(instr, frame_state_offset);
#if DEBUG
// Make sure all the values live in stack slots or they are immediates.
// (The values should not live in register because registers are clobbered
FrameStateDescriptor* descriptor =
code()->GetDeoptimizationEntry(deoptimization_id);
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());
}
#endif
- BuildTranslation(instr, first_state_value_offset, deoptimization_id);
safepoints()->RecordLazyDeoptimizationIndex(deoptimization_id);
}
}
}
-void CodeGenerator::BuildTranslation(Instruction* instr,
- int first_argument_index,
- int deoptimization_id) {
+int CodeGenerator::BuildTranslation(Instruction* instr,
+ int frame_state_offset) {
+ InstructionOperandConverter i(this, instr);
+ int deoptimization_id = i.InputInt32(frame_state_offset);
+ frame_state_offset++;
+
// We should build translation only once.
DCHECK_EQ(NULL, deoptimization_states_[deoptimization_id]);
for (int i = 0; i < descriptor->size(); i++) {
AddTranslationForOperand(&translation, instr,
- instr->InputAt(i + first_argument_index));
+ instr->InputAt(i + frame_state_offset));
}
deoptimization_states_[deoptimization_id] =
new (zone()) DeoptimizationState(translation.index());
+
+ return deoptimization_id;
}
Safepoint::Id safepoint_id);
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);
- void BuildTranslation(Instruction* instr, int first_argument_index,
- int deoptimization_id);
+ int BuildTranslation(Instruction* instr, int frame_state_offset);
void AddTranslationForOperand(Translation* translation, Instruction* instr,
InstructionOperand* op);
void AddNopForSmiCodeInlining();
break;
}
case kArchDeoptimize: {
- int deoptimization_id = MiscField::decode(instr->opcode());
- BuildTranslation(instr, 0, deoptimization_id);
+ int deoptimization_id = BuildTranslation(instr, 0);
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
DCHECK(deopt->op()->opcode() == IrOpcode::kDeoptimize);
Node* state = deopt->InputAt(0);
FrameStateDescriptor* descriptor = GetFrameStateDescriptor(state);
+ int deoptimization_id = sequence()->AddDeoptimizationEntry(descriptor);
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);
- DCHECK_EQ(descriptor->size(), inputs.size());
+ DCHECK_EQ(descriptor->size() + 1, inputs.size());
- int deoptimization_id = sequence()->AddDeoptimizationEntry(descriptor);
- Emit(kArchDeoptimize | MiscField::encode(deoptimization_id), 0, NULL,
- inputs.size(), &inputs.front(), 0, NULL);
+ Emit(kArchDeoptimize, 0, NULL, inputs.size(), &inputs.front(), 0, NULL);
}
}
+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,
Label* continuation, Label* deoptimization) {
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(1, zone());
Label* Exit();
void Goto(Label* label);
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.
Node* CallJS0(Node* function, Node* receiver, Label* continuation,
Label* deoptimization);
break;
}
case kArchDeoptimize: {
- int deoptimization_id = MiscField::decode(instr->opcode());
- BuildTranslation(instr, 0, deoptimization_id);
+ int deoptimization_id = BuildTranslation(instr, 0);
+
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
__ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
// Check that we deoptimize to the right AST id.
CHECK_EQ(1, data->DeoptCount());
- CHECK_EQ(1, data->DeoptCount());
CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
}
}
}
}
-
} // namespace compiler
} // namespace internal
} // namespace v8
continue;
}
}
+ if (mode == kAllExceptNopInstructions && instr->arch_opcode() == kArchNop) {
+ continue;
+ }
for (size_t i = 0; i < instr->OutputCount(); ++i) {
InstructionOperand* output = instr->OutputAt(i);
EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
s.references_.insert(virtual_register);
}
}
+ for (int i = 0; i < sequence.GetDeoptimizationEntryCount(); i++) {
+ s.deoptimization_entries_.push_back(sequence.GetDeoptimizationEntry(i));
+ }
return s;
}
}
}
+
+// -----------------------------------------------------------------------------
+// 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 internal
} // namespace v8
class Stream;
- enum StreamBuilderMode { kAllInstructions, kTargetInstructions };
+ enum StreamBuilderMode {
+ kAllInstructions,
+ kTargetInstructions,
+ kAllExceptNopInstructions
+ };
class StreamBuilder V8_FINAL : public RawMachineAssembler {
public:
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:
Constant ToConstant(const InstructionOperand* operand) const {
ConstantMap::const_iterator i;
std::deque<Instruction*> instructions_;
std::set<int> doubles_;
std::set<int> references_;
+ std::deque<FrameStateDescriptor*> deoptimization_entries_;
};
base::RandomNumberGenerator rng_;