Operator* op = common()->FrameState(ast_id, combine);
return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_,
- GetContext());
+ GetContext(),
+ builder()->jsgraph()->UndefinedConstant());
}
// further mutation of the environment will not affect checkpoints.
Node* Checkpoint(BailoutId ast_id, OutputFrameStateCombine combine);
+ protected:
+ AstGraphBuilder* builder() const {
+ return reinterpret_cast<AstGraphBuilder*>(
+ StructuredGraphBuilder::Environment::builder());
+ }
+
private:
void UpdateStateValues(Node** state_values, int offset, int count);
}
-int CodeGenerator::BuildTranslation(Instruction* instr, int frame_state_offset,
- OutputFrameStateCombine state_combine) {
- FrameStateDescriptor* descriptor =
- GetFrameStateDescriptor(instr, frame_state_offset);
- frame_state_offset++;
+void CodeGenerator::BuildTranslationForFrameStateDescriptor(
+ FrameStateDescriptor* descriptor, Instruction* instr,
+ Translation* translation, int frame_state_offset,
+ OutputFrameStateCombine state_combine) {
+ // Outer-most state must be added to translation first.
+ if (descriptor->outer_state() != NULL) {
+ BuildTranslationForFrameStateDescriptor(
+ descriptor->outer_state(), instr, translation,
+ frame_state_offset + descriptor->size(), kIgnoreOutput);
+ }
int height = descriptor->size() - descriptor->parameters_count();
switch (state_combine) {
break;
}
-
- Translation translation(&translations_, 1, 1, zone());
- translation.BeginJSFrame(descriptor->bailout_id(),
- Translation::kSelfLiteralId, height);
+ translation->BeginJSFrame(descriptor->bailout_id(),
+ Translation::kSelfLiteralId, height);
for (int i = 0; i < descriptor->size(); i++) {
- AddTranslationForOperand(&translation, instr,
+ AddTranslationForOperand(translation, instr,
instr->InputAt(i + frame_state_offset));
}
switch (state_combine) {
case kPushOutput:
DCHECK(instr->OutputCount() == 1);
- AddTranslationForOperand(&translation, instr, instr->OutputAt(0));
+ AddTranslationForOperand(translation, instr, instr->OutputAt(0));
break;
case kIgnoreOutput:
break;
}
+}
+
+
+int CodeGenerator::BuildTranslation(Instruction* instr, int frame_state_offset,
+ OutputFrameStateCombine state_combine) {
+ FrameStateDescriptor* descriptor =
+ GetFrameStateDescriptor(instr, frame_state_offset);
+ frame_state_offset++;
+
+ int frame_count = descriptor->GetFrameCount();
+ Translation translation(&translations_, frame_count, frame_count, zone());
+ BuildTranslationForFrameStateDescriptor(descriptor, instr, &translation,
+ frame_state_offset, state_combine);
int deoptimization_id = static_cast<int>(deoptimization_states_.size());
int frame_state_offset);
int BuildTranslation(Instruction* instr, int frame_state_offset,
OutputFrameStateCombine state_combine);
+ void BuildTranslationForFrameStateDescriptor(
+ FrameStateDescriptor* descriptor, Instruction* instr,
+ Translation* translation, int frame_state_offset,
+ OutputFrameStateCombine state_combine);
void AddTranslationForOperand(Translation* translation, Instruction* instr,
InstructionOperand* op);
void AddNopForSmiCodeInlining();
DCHECK(OperatorProperties::GetEffectInputCount(op) < 2);
Node* result = NULL;
- if (!has_context && !has_control && !has_effect) {
+ if (!has_context && !has_framestate && !has_control && !has_effect) {
result = graph()->NewNode(op, value_input_count, value_inputs);
} else {
int input_count_with_deps = value_input_count;
size_t frame_state_value_count() const {
return (frame_state_descriptor == NULL)
? 0
- : (frame_state_descriptor->size() + 1);
+ : (frame_state_descriptor->total_size() +
+ 1); // Include deopt id.
}
};
Node* stack = m.NewNode(m.common()->StateValues(0));
Node* context_dummy = m.Int32Constant(0);
- Node* state_node = m.NewNode(m.common()->FrameState(bailout_id, kPushOutput),
- parameters, locals, stack, context_dummy);
+ Node* state_node =
+ m.NewNode(m.common()->FrameState(bailout_id, kPushOutput), parameters,
+ locals, stack, context_dummy, m.UndefinedConstant());
Node* call = m.CallJS0(function_node, receiver, context, state_node);
m.Return(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* context_sentinel = m.Int32Constant(0);
- Node* frame_state_before =
- m.NewNode(m.common()->FrameState(bailout_id_before, kPushOutput),
- parameters, locals, stack, context_sentinel);
+ Node* frame_state_before = m.NewNode(
+ m.common()->FrameState(bailout_id_before, kPushOutput), parameters,
+ locals, stack, context_sentinel, m.UndefinedConstant());
// Build the call.
Node* call = m.CallFunctionStub0(function_node, receiver, context,
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_parent(62);
+
+ // Some arguments for the call node.
+ Node* function_node = m.Parameter(0);
+ Node* receiver = m.Parameter(1);
+ Node* context = m.Int32Constant(66);
+
+ // 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, kIgnoreOutput),
+ parameters, locals, stack, context, m.UndefinedConstant());
+
+ Node* context2 = m.Int32Constant(46);
+ 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, kPushOutput),
+ parameters2, locals2, stack2, context2, frame_state_parent);
+
+ // Build the call.
+ Node* call = m.CallFunctionStub0(function_node, receiver, context2,
+ frame_state_before, CALL_AS_METHOD);
+
+ m.Return(call);
+
+ 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.
+ EXPECT_EQ(index + 2, 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
+ 4 + // One input for each value in frame state + context.
+ 4 + // One input for each value in the parent frame state + context.
+ 1 + // Function.
+ 1; // Context.
+ EXPECT_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.GetFrameStateDescriptor(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(63, s.ToInt32(call_instr->InputAt(2)));
+ // Context:
+ EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(3)));
+ EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(4)));
+ EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(5)));
+ // Values from parent environment should follow.
+ EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(6)));
+ EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(7)));
+ EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(8)));
+ EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(9)));
+
+ // Function.
+ EXPECT_EQ(function_node->id(), s.ToVreg(call_instr->InputAt(10)));
+ // Context.
+ EXPECT_EQ(context2->id(), s.ToVreg(call_instr->InputAt(11)));
+ // Continuation.
+
+ EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
+ EXPECT_EQ(index, s.size());
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8
FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor(
Node* state) {
- DCHECK(state->op()->opcode() == IrOpcode::kFrameState);
+ DCHECK(state->opcode() == IrOpcode::kFrameState);
+ DCHECK_EQ(5, state->InputCount());
FrameStateCallInfo state_info = OpParameter<FrameStateCallInfo>(state);
- Node* parameters = state->InputAt(0);
- Node* locals = state->InputAt(1);
- Node* stack = state->InputAt(2);
+ int parameters = OpParameter<int>(state->InputAt(0));
+ int locals = OpParameter<int>(state->InputAt(1));
+ int stack = OpParameter<int>(state->InputAt(2));
+
+ FrameStateDescriptor* outer_state = NULL;
+ Node* outer_node = state->InputAt(4);
+ if (outer_node->opcode() == IrOpcode::kFrameState) {
+ outer_state = GetFrameStateDescriptor(outer_node);
+ }
return new (instruction_zone())
- FrameStateDescriptor(state_info, OpParameter<int>(parameters),
- OpParameter<int>(locals), OpParameter<int>(stack));
+ FrameStateDescriptor(state_info, parameters, locals, stack, outer_state);
}
FrameStateDescriptor* descriptor) {
DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode());
+ if (descriptor->outer_state() != NULL) {
+ AddFrameStateInputs(state->InputAt(4), inputs, descriptor->outer_state());
+ }
+
Node* parameters = state->InputAt(0);
Node* locals = state->InputAt(1);
Node* stack = state->InputAt(2);
class FrameStateDescriptor : public ZoneObject {
public:
FrameStateDescriptor(const FrameStateCallInfo& state_info,
- int parameters_count, int locals_count, int stack_count)
+ int parameters_count, int locals_count, int stack_count,
+ FrameStateDescriptor* outer_state = NULL)
: bailout_id_(state_info.bailout_id()),
frame_state_combine_(state_info.state_combine()),
parameters_count_(parameters_count),
locals_count_(locals_count),
- stack_count_(stack_count) {}
+ stack_count_(stack_count),
+ outer_state_(outer_state) {}
BailoutId bailout_id() const { return bailout_id_; }
OutputFrameStateCombine state_combine() const { return frame_state_combine_; }
int parameters_count() { return parameters_count_; }
int locals_count() { return locals_count_; }
int stack_count() { return stack_count_; }
+ FrameStateDescriptor* outer_state() { return outer_state_; }
+ void set_outer_state(FrameStateDescriptor* outer_state) {
+ outer_state_ = outer_state;
+ }
int size() {
return parameters_count_ + locals_count_ + stack_count_ +
1; // Includes context.
}
+ int total_size() {
+ int total_size = 0;
+ for (FrameStateDescriptor* iter = this; iter != NULL;
+ iter = iter->outer_state_) {
+ total_size += iter->size();
+ }
+ return total_size;
+ }
+
+ int GetFrameCount() {
+ int count = 0;
+ for (FrameStateDescriptor* iter = this; iter != NULL;
+ iter = iter->outer_state_) {
+ ++count;
+ }
+ return count;
+ }
+
private:
BailoutId bailout_id_;
OutputFrameStateCombine frame_state_combine_;
int parameters_count_;
int locals_count_;
int stack_count_;
+ FrameStateDescriptor* outer_state_;
};
OStream& operator<<(OStream& os, const Constant& constant);
typedef Node::Uses::iterator UseIter;
typedef Node::Inputs::iterator InputIter;
+// Helper to extract parameters from Operator1<*> operator.
+template <typename T>
+static inline T OpParameter(Operator* op) {
+ return reinterpret_cast<Operator1<T>*>(op)->parameter();
+}
+
+
// Helper to extract parameters from Operator1<*> nodes.
template <typename T>
static inline T OpParameter(Node* node) {
- return reinterpret_cast<Operator1<T>*>(node->op())->parameter();
+ return OpParameter<T>(node->op());
}
}
}
}
switch (op->opcode()) {
+ case IrOpcode::kFrameState:
+ return true;
case IrOpcode::kJSCallRuntime: {
Runtime::FunctionId function =
reinterpret_cast<Operator1<Runtime::FunctionId>*>(op)->parameter();
size_t parameter_count() const { return machine_sig_->parameter_count(); }
MachineSignature* machine_sig() const { return machine_sig_; }
+ Node* UndefinedConstant() {
+ PrintableUnique<Object> unique = PrintableUnique<Object>::CreateImmovable(
+ zone(), isolate()->factory()->undefined_value());
+ return NewNode(common()->HeapConstant(unique));
+ }
+
// Parameters.
Node* Parameter(size_t index);
// Verify that frame state has been inserted for the nodes that need it.
if (OperatorProperties::HasFrameStateInput(node->op())) {
Node* frame_state = NodeProperties::GetFrameStateInput(node);
- CHECK(frame_state->opcode() == IrOpcode::kFrameState);
+ CHECK(frame_state->opcode() == IrOpcode::kFrameState ||
+ // kFrameState uses undefined as a sentinel.
+ (node->opcode() == IrOpcode::kFrameState &&
+ frame_state->opcode() == IrOpcode::kHeapConstant));
CHECK(IsDefUseChainLinkPresent(frame_state, node));
CHECK(IsUseDefChainLinkPresent(frame_state, node));
}
}
Schedule* BuildGraphAndSchedule(Graph* graph) {
- Isolate* isolate = info.isolate();
CommonOperatorBuilder common(zone());
// Manually construct a schedule for the function below:
CSignature1<Object*, Object*> sig;
RawMachineAssembler m(graph, &sig);
- Handle<Object> undef_object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- PrintableUnique<Object> undef_constant =
- PrintableUnique<Object>::CreateUninitialized(zone(), undef_object);
- Node* undef_node = m.NewNode(common.HeapConstant(undef_constant));
-
Handle<JSFunction> deopt_function =
NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
PrintableUnique<Object> deopt_fun_constant =
PrintableUnique<Object>::CreateUninitialized(zone(), deopt_function);
Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
- Handle<Context> context(deopt_function->context(), isolate);
+ Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
PrintableUnique<Object> context_constant =
PrintableUnique<Object>::CreateUninitialized(zone(), context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
bailout_id = GetCallBailoutId();
- Node* parameters = m.NewNode(common.StateValues(1), undef_node);
+ Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
Node* locals = m.NewNode(common.StateValues(0));
Node* stack = m.NewNode(common.StateValues(0));
- Node* state_node = m.NewNode(common.FrameState(bailout_id, kIgnoreOutput),
- parameters, locals, stack, undef_node);
+ Node* state_node =
+ m.NewNode(common.FrameState(bailout_id, kIgnoreOutput), parameters,
+ locals, stack, m.UndefinedConstant(), m.UndefinedConstant());
- m.CallJS0(deopt_fun_node, undef_node, context_node, state_node);
+ m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
- m.Return(undef_node);
+ m.Return(m.UndefinedConstant());
// Schedule the graph:
Schedule* schedule = m.Export();
}
Schedule* BuildGraphAndSchedule(Graph* graph) {
- Isolate* isolate = info.isolate();
CommonOperatorBuilder common(zone());
// Manually construct a schedule for the function below:
CSignature1<Object*, Object*> sig;
RawMachineAssembler m(graph, &sig);
- Handle<Object> undef_object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- PrintableUnique<Object> undef_constant =
- PrintableUnique<Object>::CreateUninitialized(zone(), undef_object);
- Node* undef_node = m.NewNode(common.HeapConstant(undef_constant));
-
PrintableUnique<Object> this_fun_constant =
PrintableUnique<Object>::CreateUninitialized(zone(), function);
Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
- Handle<Context> context(function->context(), isolate);
+ Handle<Context> context(function->context(), CcTest::i_isolate());
PrintableUnique<Object> context_constant =
PrintableUnique<Object>::CreateUninitialized(zone(), context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
bailout_id = GetCallBailoutId();
- Node* parameters = m.NewNode(common.StateValues(1), undef_node);
+ Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
Node* locals = m.NewNode(common.StateValues(0));
Node* stack = m.NewNode(common.StateValues(0));
- Node* state_node = m.NewNode(common.FrameState(bailout_id, kIgnoreOutput),
- parameters, locals, stack, undef_node);
+ Node* state_node =
+ m.NewNode(common.FrameState(bailout_id, kIgnoreOutput), parameters,
+ locals, stack, m.UndefinedConstant(), m.UndefinedConstant());
m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
state_node);
- m.Return(undef_node);
+ m.Return(m.UndefinedConstant());
// Schedule the graph:
Schedule* schedule = m.Export();