UpdateStateValues(&stack_node_, parameters_count() + locals_count(),
stack_height());
- const Operator* op = common()->FrameState(ast_id, combine);
+ const Operator* op = common()->FrameState(JS_FRAME, ast_id, combine);
return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_,
GetContext(),
void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id,
OutputFrameStateCombine combine) {
if (OperatorProperties::HasFrameStateInput(node->op())) {
- int frame_state_index = NodeProperties::GetFrameStateIndex(node);
-
- DCHECK(node->InputAt(frame_state_index)->op()->opcode() == IrOpcode::kDead);
-
- Node* frame_state_node = environment()->Checkpoint(ast_id, combine);
- node->ReplaceInput(frame_state_index, frame_state_node);
+ DCHECK(NodeProperties::GetFrameStateInput(node)->opcode() ==
+ IrOpcode::kDead);
+ NodeProperties::ReplaceFrameStateInput(
+ node, environment()->Checkpoint(ast_id, combine));
}
}
// (just after the code address).
InstructionOperandConverter converter(this, instr);
// Deoptimization info starts at argument 1
- int frame_state_offset = 1;
+ size_t frame_state_offset = 1;
FrameStateDescriptor* descriptor =
GetFrameStateDescriptor(instr, frame_state_offset);
int pc_offset = masm()->pc_offset();
// Make sure all the values live in stack slots or they are immediates.
// (The values should not live in register because registers are clobbered
// by calls.)
- for (int i = 0; i < descriptor->size(); i++) {
+ for (size_t i = 0; i < descriptor->size(); i++) {
InstructionOperand* op = instr->InputAt(frame_state_offset + 1 + i);
CHECK(op->IsStackSlot() || op->IsImmediate());
}
FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor(
- Instruction* instr, int frame_state_offset) {
+ Instruction* instr, size_t frame_state_offset) {
InstructionOperandConverter i(this, instr);
- InstructionSequence::StateId state_id =
- InstructionSequence::StateId::FromInt(i.InputInt32(frame_state_offset));
+ InstructionSequence::StateId state_id = InstructionSequence::StateId::FromInt(
+ i.InputInt32(static_cast<int>(frame_state_offset)));
return code()->GetFrameStateDescriptor(state_id);
}
void CodeGenerator::BuildTranslationForFrameStateDescriptor(
FrameStateDescriptor* descriptor, Instruction* instr,
- Translation* translation, int frame_state_offset,
+ Translation* translation, size_t 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);
+ BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), instr,
+ translation, frame_state_offset,
+ kIgnoreOutput);
}
- int height = descriptor->size() - descriptor->parameters_count();
- switch (state_combine) {
- case kPushOutput:
- height++;
+ int id = Translation::kSelfLiteralId;
+ if (!descriptor->jsfunction().is_null()) {
+ id = DefineDeoptimizationLiteral(
+ Handle<Object>::cast(descriptor->jsfunction().ToHandleChecked()));
+ }
+
+ switch (descriptor->type()) {
+ case JS_FRAME:
+ translation->BeginJSFrame(
+ descriptor->bailout_id(), id,
+ static_cast<unsigned int>(descriptor->GetHeight(state_combine)));
break;
- case kIgnoreOutput:
+ case ARGUMENTS_ADAPTOR:
+ translation->BeginArgumentsAdaptorFrame(
+ id, static_cast<unsigned int>(descriptor->parameters_count()));
break;
}
- translation->BeginJSFrame(descriptor->bailout_id(),
- Translation::kSelfLiteralId, height);
-
- for (int i = 0; i < descriptor->size(); i++) {
- AddTranslationForOperand(translation, instr,
- instr->InputAt(i + frame_state_offset));
+ frame_state_offset += descriptor->outer_state()->GetTotalSize();
+ for (size_t i = 0; i < descriptor->size(); i++) {
+ AddTranslationForOperand(
+ translation, instr,
+ instr->InputAt(static_cast<int>(frame_state_offset + i)));
}
switch (state_combine) {
int CodeGenerator::BuildTranslation(Instruction* instr, int pc_offset,
- int frame_state_offset,
+ size_t 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());
+ Translation translation(
+ &translations_, static_cast<int>(descriptor->GetFrameCount()),
+ static_cast<int>(descriptor->GetJSFrameCount()), zone());
BuildTranslationForFrameStateDescriptor(descriptor, instr, &translation,
frame_state_offset, state_combine);
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);
FrameStateDescriptor* GetFrameStateDescriptor(Instruction* instr,
- int frame_state_offset);
+ size_t frame_state_offset);
int BuildTranslation(Instruction* instr, int pc_offset,
- int frame_state_offset,
+ size_t frame_state_offset,
OutputFrameStateCombine state_combine);
void BuildTranslationForFrameStateDescriptor(
FrameStateDescriptor* descriptor, Instruction* instr,
- Translation* translation, int frame_state_offset,
+ Translation* translation, size_t frame_state_offset,
OutputFrameStateCombine state_combine);
void AddTranslationForOperand(Translation* translation, Instruction* instr,
InstructionOperand* op);
const Operator* CommonOperatorBuilder::FrameState(
- BailoutId bailout_id, OutputFrameStateCombine combine) {
+ FrameStateType type, BailoutId bailout_id,
+ OutputFrameStateCombine state_combine, MaybeHandle<JSFunction> jsfunction) {
return new (zone()) Operator1<FrameStateCallInfo>(
IrOpcode::kFrameState, Operator::kPure, 4, 1, "FrameState",
- FrameStateCallInfo(bailout_id, combine));
+ FrameStateCallInfo(type, bailout_id, state_combine, jsfunction));
}
#define V8_COMPILER_COMMON_OPERATOR_H_
#include "src/compiler/machine-type.h"
+#include "src/unique.h"
namespace v8 {
namespace internal {
// Forward declarations.
class ExternalReference;
class OStream;
-template <typename>
-class Unique;
-class Zone;
namespace compiler {
};
+// The type of stack frame that a FrameState node represents.
+enum FrameStateType {
+ JS_FRAME, // Represents an unoptimized JavaScriptFrame.
+ ARGUMENTS_ADAPTOR // Represents an ArgumentsAdaptorFrame.
+};
+
+
class FrameStateCallInfo FINAL {
public:
- FrameStateCallInfo(BailoutId bailout_id,
- OutputFrameStateCombine state_combine)
- : bailout_id_(bailout_id), frame_state_combine_(state_combine) {}
-
+ FrameStateCallInfo(
+ FrameStateType type, BailoutId bailout_id,
+ OutputFrameStateCombine state_combine,
+ MaybeHandle<JSFunction> jsfunction = MaybeHandle<JSFunction>())
+ : type_(type),
+ bailout_id_(bailout_id),
+ frame_state_combine_(state_combine),
+ jsfunction_(jsfunction) {}
+
+ FrameStateType type() const { return type_; }
BailoutId bailout_id() const { return bailout_id_; }
OutputFrameStateCombine state_combine() const { return frame_state_combine_; }
+ MaybeHandle<JSFunction> jsfunction() const { return jsfunction_; }
private:
+ FrameStateType type_;
BailoutId bailout_id_;
OutputFrameStateCombine frame_state_combine_;
+ MaybeHandle<JSFunction> jsfunction_;
};
const Operator* ValueEffect(int arguments);
const Operator* Finish(int arguments);
const Operator* StateValues(int arguments);
- const Operator* FrameState(BailoutId bailout_id,
- OutputFrameStateCombine combine);
+ const Operator* FrameState(
+ FrameStateType type, BailoutId bailout_id,
+ OutputFrameStateCombine state_combine,
+ MaybeHandle<JSFunction> jsfunction = MaybeHandle<JSFunction>());
const Operator* Call(const CallDescriptor* descriptor);
const Operator* Projection(size_t index);
size_t frame_state_value_count() const {
return (frame_state_descriptor == NULL)
? 0
- : (frame_state_descriptor->total_size() +
+ : (frame_state_descriptor->GetTotalSize() +
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, m.UndefinedConstant());
+ Node* state_node = m.NewNode(
+ m.common()->FrameState(JS_FRAME, bailout_id, kPushOutput), parameters,
+ locals, stack, context_dummy, m.UndefinedConstant());
Node* call = m.CallJS0(function_node, receiver, context, state_node);
m.Return(call);
Node* context_sentinel = m.Int32Constant(0);
Node* frame_state_before = m.NewNode(
- m.common()->FrameState(bailout_id_before, kPushOutput), parameters,
- locals, stack, context_sentinel, m.UndefinedConstant());
+ m.common()->FrameState(JS_FRAME, bailout_id_before, kPushOutput),
+ parameters, locals, stack, context_sentinel, m.UndefinedConstant());
// Build the call.
Node* call = m.CallFunctionStub0(function_node, receiver, context,
s.GetFrameStateDescriptor(deopt_id_before);
EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
EXPECT_EQ(kPushOutput, desc_before->state_combine());
- EXPECT_EQ(1, desc_before->parameters_count());
- EXPECT_EQ(1, desc_before->locals_count());
- EXPECT_EQ(1, desc_before->stack_count());
+ EXPECT_EQ(1u, desc_before->parameters_count());
+ EXPECT_EQ(1u, desc_before->locals_count());
+ EXPECT_EQ(1u, desc_before->stack_count());
EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(2)));
EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(3)));
EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(4)));
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* frame_state_parent = m.NewNode(
+ m.common()->FrameState(JS_FRAME, 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);
+ Node* frame_state_before = m.NewNode(
+ m.common()->FrameState(JS_FRAME, bailout_id_before, kPushOutput),
+ parameters2, locals2, stack2, context2, frame_state_parent);
// Build the call.
Node* call = m.CallFunctionStub0(function_node, receiver, context2,
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(1u, desc_before->parameters_count());
+ EXPECT_EQ(1u, desc_before->locals_count());
+ EXPECT_EQ(1u, desc_before->stack_count());
EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(2)));
// Context:
EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(3)));
DCHECK_EQ(descriptor->stack_count(), stack->InputCount());
OperandGenerator g(this);
- for (int i = 0; i < descriptor->parameters_count(); i++) {
+ for (int i = 0; i < static_cast<int>(descriptor->parameters_count()); i++) {
inputs->push_back(UseOrImmediate(&g, parameters->InputAt(i)));
}
- inputs->push_back(UseOrImmediate(&g, context));
- for (int i = 0; i < descriptor->locals_count(); i++) {
+ if (descriptor->HasContext()) {
+ inputs->push_back(UseOrImmediate(&g, context));
+ }
+ for (int i = 0; i < static_cast<int>(descriptor->locals_count()); i++) {
inputs->push_back(UseOrImmediate(&g, locals->InputAt(i)));
}
- for (int i = 0; i < descriptor->stack_count(); i++) {
+ for (int i = 0; i < static_cast<int>(descriptor->stack_count()); i++) {
inputs->push_back(UseOrImmediate(&g, stack->InputAt(i)));
}
}
class FrameStateDescriptor : public ZoneObject {
public:
FrameStateDescriptor(const FrameStateCallInfo& state_info,
- int parameters_count, int locals_count, int stack_count,
+ size_t parameters_count, size_t locals_count,
+ size_t stack_count,
FrameStateDescriptor* outer_state = NULL)
- : bailout_id_(state_info.bailout_id()),
+ : type_(state_info.type()),
+ 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),
- outer_state_(outer_state) {}
+ outer_state_(outer_state),
+ jsfunction_(state_info.jsfunction()) {}
+ FrameStateType type() const { return type_; }
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;
- }
+ size_t parameters_count() const { return parameters_count_; }
+ size_t locals_count() const { return locals_count_; }
+ size_t stack_count() const { return stack_count_; }
+ FrameStateDescriptor* outer_state() const { return outer_state_; }
+ MaybeHandle<JSFunction> jsfunction() const { return jsfunction_; }
- int size() {
+ size_t size() const {
return parameters_count_ + locals_count_ + stack_count_ +
- 1; // Includes context.
+ (HasContext() ? 1 : 0);
}
- int total_size() {
- int total_size = 0;
- for (FrameStateDescriptor* iter = this; iter != NULL;
+ size_t GetTotalSize() const {
+ size_t total_size = 0;
+ for (const 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;
+ size_t GetHeight(OutputFrameStateCombine override) const {
+ size_t height = size() - parameters_count();
+ switch (override) {
+ case kPushOutput:
+ ++height;
+ break;
+ case kIgnoreOutput:
+ break;
+ }
+ return height;
+ }
+
+ size_t GetFrameCount() const {
+ size_t count = 0;
+ for (const FrameStateDescriptor* iter = this; iter != NULL;
iter = iter->outer_state_) {
++count;
}
return count;
}
+ size_t GetJSFrameCount() const {
+ size_t count = 0;
+ for (const FrameStateDescriptor* iter = this; iter != NULL;
+ iter = iter->outer_state_) {
+ if (iter->type_ == JS_FRAME) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ bool HasContext() const { return type_ == JS_FRAME; }
+
private:
+ FrameStateType type_;
BailoutId bailout_id_;
OutputFrameStateCombine frame_state_combine_;
- int parameters_count_;
- int locals_count_;
- int stack_count_;
+ size_t parameters_count_;
+ size_t locals_count_;
+ size_t stack_count_;
FrameStateDescriptor* outer_state_;
+ MaybeHandle<JSFunction> jsfunction_;
};
OStream& operator<<(OStream& os, const Constant& constant);
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/typer.h"
+#include "src/full-codegen.h"
#include "src/parser.h"
#include "src/rewriter.h"
#include "src/scopes.h"
// test cases, where similar code is currently duplicated).
static void Parse(Handle<JSFunction> function, CompilationInfoWithZone* info) {
CHECK(Parser::Parse(info));
- info->SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
CHECK(Rewriter::Rewrite(info));
CHECK(Scope::Analyze(info));
CHECK_NE(NULL, info->scope());
DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode());
return unique_return;
}
+
+ // Counts JSFunction, Receiver, arguments, context but not effect, control.
+ size_t total_parameters() { return start_->op()->OutputCount(); }
+
+ // Counts only formal parameters.
+ size_t formal_parameters() {
+ DCHECK_GE(total_parameters(), 3);
+ return total_parameters() - 3;
+ }
+
// Inline this graph at {call}, use {jsgraph} and its zone to create
// any new nodes.
void InlineAtCall(JSGraph* jsgraph, Node* call);
private:
void ReplaceSentinels() {
- for (int id = 0; id < source_graph_->NodeCount(); ++id) {
+ for (NodeId id = 0; id < source_graph_->NodeCount(); ++id) {
Node* sentinel = sentinels_[id];
if (sentinel == NULL) continue;
Node* copy = copies_[id];
NodeProperties::GetValueInput(call, 0),
NodeProperties::GetEffectInput(call));
- // {inlinee_inputs} counts JSFunction, Receiver, arguments, context,
- // but not effect, control.
- int inlinee_inputs = start_->op()->OutputCount();
// Context is last argument.
- int inlinee_context_index = inlinee_inputs - 1;
+ int inlinee_context_index = static_cast<int>(total_parameters()) - 1;
// {inliner_inputs} counts JSFunction, Receiver, arguments, but not
// context, effect, control.
int inliner_inputs = OperatorProperties::GetValueInputCount(call->op());
}
-void JSInliner::TryInlineCall(Node* call) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode());
+// TODO(turbofan) Provide such accessors for every node, possibly even
+// generate them.
+class JSCallFunctionAccessor {
+ public:
+ explicit JSCallFunctionAccessor(Node* call) : call_(call) {
+ DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode());
+ }
+
+ Node* jsfunction() { return call_->InputAt(0); }
+
+ Node* receiver() { return call_->InputAt(1); }
+
+ Node* formal_argument(size_t index) {
+ DCHECK(index < formal_arguments());
+ return call_->InputAt(static_cast<int>(2 + index));
+ }
+
+ size_t formal_arguments() {
+ // {value_inputs} includes jsfunction and receiver.
+ size_t value_inputs = OperatorProperties::GetValueInputCount(call_->op());
+ DCHECK_GE(call_->InputCount(), 2);
+ return value_inputs - 2;
+ }
+
+ Node* frame_state() { return NodeProperties::GetFrameStateInput(call_); }
+
+ private:
+ Node* call_;
+};
+
+
+void JSInliner::AddClosureToFrameState(Node* frame_state,
+ Handle<JSFunction> jsfunction) {
+ FrameStateCallInfo call_info = OpParameter<FrameStateCallInfo>(frame_state);
+ const Operator* op = jsgraph_->common()->FrameState(
+ FrameStateType::JS_FRAME, call_info.bailout_id(),
+ call_info.state_combine(), jsfunction);
+ frame_state->set_op(op);
+}
+
+
+Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call,
+ Handle<JSFunction> jsfunction,
+ Zone* temp_zone) {
+ const Operator* op =
+ jsgraph_->common()->FrameState(FrameStateType::ARGUMENTS_ADAPTOR,
+ BailoutId(-1), kIgnoreOutput, jsfunction);
+ const Operator* op0 = jsgraph_->common()->StateValues(0);
+ Node* node0 = jsgraph_->graph()->NewNode(op0);
+ NodeVector params(temp_zone);
+ params.push_back(call->receiver());
+ for (size_t argument = 0; argument != call->formal_arguments(); ++argument) {
+ params.push_back(call->formal_argument(argument));
+ }
+ const Operator* op_param =
+ jsgraph_->common()->StateValues(static_cast<int>(params.size()));
+ Node* params_node = jsgraph_->graph()->NewNode(
+ op_param, static_cast<int>(params.size()), ¶ms.front());
+ return jsgraph_->graph()->NewNode(op, params_node, node0, node0,
+ jsgraph_->UndefinedConstant(),
+ call->frame_state());
+}
+
- HeapObjectMatcher<JSFunction> match(call->InputAt(0));
+void JSInliner::TryInlineCall(Node* call_node) {
+ JSCallFunctionAccessor call(call_node);
+
+ HeapObjectMatcher<JSFunction> match(call.jsfunction());
if (!match.HasValue()) {
return;
}
CompilationInfoWithZone info(function);
Parse(function, &info);
+ if (!function->shared()->has_deoptimization_support()) {
+ // TODO(turbofan) In the future, unoptimized code with deopt support could
+ // be generated lazily once deopt is triggered.
+ info.EnableDeoptimizationSupport();
+ if (!FullCodeGenerator::MakeCode(&info)) {
+ DCHECK(false);
+ return;
+ }
+ function->shared()->EnableDeoptimizationSupport(*info.code());
+ function->shared()->set_feedback_vector(*info.feedback_vector());
+ }
+
if (info.scope()->arguments() != NULL) {
// For now do not inline functions that use their arguments array.
SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
visitor.CopyGraph();
Inlinee inlinee(visitor.GetCopy(graph.start()), visitor.GetCopy(graph.end()));
- inlinee.InlineAtCall(jsgraph_, call);
+
+ Node* outer_frame_state = call.frame_state();
+ // Insert argument adaptor frame if required.
+ if (call.formal_arguments() != inlinee.formal_parameters()) {
+ outer_frame_state =
+ CreateArgumentsAdaptorFrameState(&call, function, info.zone());
+ }
+
+ for (NodeVectorConstIter it = visitor.copies().begin();
+ it != visitor.copies().end(); ++it) {
+ Node* node = *it;
+ if (node != NULL && node->opcode() == IrOpcode::kFrameState) {
+ AddClosureToFrameState(node, function);
+ NodeProperties::ReplaceFrameStateInput(node, outer_frame_state);
+ }
+ }
+
+ inlinee.InlineAtCall(jsgraph_, call_node);
}
}
}
namespace internal {
namespace compiler {
+class JSCallFunctionAccessor;
+
class JSInliner {
public:
JSInliner(CompilationInfo* info, JSGraph* jsgraph)
CompilationInfo* info_;
JSGraph* jsgraph_;
+ Node* CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call,
+ Handle<JSFunction> jsfunction,
+ Zone* temp_zone);
+ void AddClosureToFrameState(Node* frame_state, Handle<JSFunction> jsfunction);
static void UnifyReturn(Graph* graph);
};
}
case Runtime::kPrepareStep:
case Runtime::kSetScriptBreakPoint:
case Runtime::kStackGuard:
+ case Runtime::kCheckExecutionState:
+ case Runtime::kDebugEvaluate:
+ case Runtime::kCollectStackTrace:
return true;
default:
return false;
return node->ReplaceInput(FirstEffectIndex(node) + index, effect);
}
+inline void NodeProperties::ReplaceFrameStateInput(Node* node,
+ Node* frame_state) {
+ DCHECK(OperatorProperties::HasFrameStateInput(node->op()));
+ node->ReplaceInput(FirstFrameStateIndex(node), frame_state);
+}
+
inline void NodeProperties::RemoveNonValueInputs(Node* node) {
node->TrimInputCount(OperatorProperties::GetValueInputCount(node->op()));
}
static inline void ReplaceControlInput(Node* node, Node* control);
static inline void ReplaceEffectInput(Node* node, Node* effect,
int index = 0);
+ static inline void ReplaceFrameStateInput(Node* node, Node* frame_state);
static inline void RemoveNonValueInputs(Node* node);
static inline void ReplaceWithValue(Node* node, Node* value,
Node* effect = NULL);
typedef ZoneVector<Node*> NodeVector;
typedef NodeVector::iterator NodeVectorIter;
+typedef NodeVector::const_iterator NodeVectorConstIter;
typedef NodeVector::reverse_iterator NodeVectorRIter;
typedef ZoneVector<NodeVector> NodeVectorVector;
DCHECK(frames->length() == 0);
DCHECK(is_optimized());
- // Delegate to JS frame in absence of inlining.
- // TODO(turbofan): Revisit once we support inlining.
- if (LookupCode()->is_turbofanned()) {
+ // Delegate to JS frame in absence of turbofan deoptimization.
+ // TODO(turbofan): Revisit once we support deoptimization across the board.
+ if (LookupCode()->is_turbofanned() && !FLAG_turbo_deoptimization) {
return JavaScriptFrame::Summarize(frames);
}
int OptimizedFrame::GetInlineCount() {
DCHECK(is_optimized());
- // Delegate to JS frame in absence of inlining.
- // TODO(turbofan): Revisit once we support inlining.
- if (LookupCode()->is_turbofanned()) {
+ // Delegate to JS frame in absence of turbofan deoptimization.
+ // TODO(turbofan): Revisit once we support deoptimization across the board.
+ if (LookupCode()->is_turbofanned() && !FLAG_turbo_deoptimization) {
return JavaScriptFrame::GetInlineCount();
}
DCHECK(functions->length() == 0);
DCHECK(is_optimized());
- // Delegate to JS frame in absence of inlining.
- // TODO(turbofan): Revisit once we support inlining.
- if (LookupCode()->is_turbofanned()) {
+ // Delegate to JS frame in absence of turbofan deoptimization.
+ // TODO(turbofan): Revisit once we support deoptimization across the board.
+ if (LookupCode()->is_turbofanned() && !FLAG_turbo_deoptimization) {
return JavaScriptFrame::GetFunctions(functions);
}
##############################################################################
# TurboFan compiler failures.
- # Scheduler cannot handle free-floating loops yet
- 'test-run-inlining/InlineLoop': [SKIP],
-
# TODO(dcarney): C calls are broken all over the place.
'test-run-machops/RunCall*': [SKIP],
'test-run-machops/RunLoadImmIndex': [SKIP],
'test-run-machops/RunSpillLotsOfThingsWithCall': [SKIP],
- # TODO(sigurds): The schedule is borked with multiple inlinees.
+ # TODO(sigurds): The schedule is borked with multiple inlinees,
+ # and cannot handle free-floating loops yet
'test-run-inlining/InlineTwiceDependentDiamond': [SKIP],
'test-run-inlining/InlineTwiceDependentDiamondDifferent': [SKIP],
+ 'test-run-inlining/InlineLoop': [SKIP],
# Some tests are just too slow to run for now.
'test-api/Threading*': [PASS, NO_VARIANTS],
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, caller_context_node, m.UndefinedConstant());
+ Node* state_node = m.NewNode(
+ common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
+ locals, stack, caller_context_node, m.UndefinedConstant());
Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
Unique<Object> context_constant =
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, context_node, m.UndefinedConstant());
+ Node* state_node = m.NewNode(
+ common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
+ locals, stack, context_node, m.UndefinedConstant());
m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
state_node);
Node* stack = graph.NewNode(common.StateValues(0));
Node* state_node =
- graph.NewNode(common.FrameState(BailoutId(0), kIgnoreOutput),
+ graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0), kIgnoreOutput),
parameters, locals, stack, context, UndefinedConstant());
return state_node;
using namespace v8::internal;
using namespace v8::internal::compiler;
-// TODO(sigurds) At the moment we do not write optimization frames when
-// inlining, thus the reported stack depth changes depending on inlining.
-// AssertStackDepth checks the stack depth actually changes as a simple way
-// to ensure that inlining actually occurs.
-// Once inlining creates optimization frames, all these unit tests need to
-// check that the optimization frame is there.
-
-
-static void AssertStackDepth(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::HandleScope scope(args.GetIsolate());
- v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
- args.GetIsolate(), 10, v8::StackTrace::kDetailed);
- CHECK_EQ(args[0]->ToInt32()->Value(), stackTrace->GetFrameCount());
+// Helper to determine inline count via JavaScriptFrame::GetInlineCount.
+// Note that a count of 1 indicates that no inlining has occured.
+static void AssertInlineCount(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ StackTraceFrameIterator it(CcTest::i_isolate());
+ int frames_seen = 0;
+ JavaScriptFrame* topmost = it.frame();
+ while (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ PrintF("%d %s, inline count: %d\n", frames_seen,
+ frame->function()->shared()->DebugName()->ToCString().get(),
+ frame->GetInlineCount());
+ frames_seen++;
+ it.Advance();
+ }
+ CHECK_EQ(args[0]->ToInt32()->Value(), topmost->GetInlineCount());
}
-static void InstallAssertStackDepthHelper(v8::Isolate* isolate) {
+static void InstallAssertInlineCountHelper(v8::Isolate* isolate) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::FunctionTemplate> t =
- v8::FunctionTemplate::New(isolate, AssertStackDepth);
- context->Global()->Set(v8_str("AssertStackDepth"), t->GetFunction());
+ v8::FunctionTemplate::New(isolate, AssertInlineCount);
+ context->Global()->Set(v8_str("AssertInlineCount"), t->GetFunction());
}
TEST(SimpleInlining) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function(){"
- "function foo(s) { AssertStackDepth(1); return s; };"
+ "function foo(s) { AssertInlineCount(2); return s; };"
"function bar(s, t) { return foo(s); };"
"return bar;})();",
CompilationInfo::kInliningEnabled |
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
+}
+
+
+TEST(SimpleInliningDeopt) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function(){"
+ "function foo(s) { %DeoptimizeFunction(bar); return "
+ "s; };"
+ "function bar(s, t) { return foo(s); };"
+ "return bar;})();",
+ CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
}
TEST(SimpleInliningContext) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { AssertStackDepth(1); var x = 12; return s + x; };"
+ "function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
"function bar(s, t) { return foo(s); };"
"return bar;"
"})();",
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
+}
+
+
+TEST(SimpleInliningContextDeopt) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ "function foo(s) { "
+ " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
+ " return s + x;"
+ "};"
+ "function bar(s, t) { return foo(s); };"
+ "return bar;"
+ "})();",
+ CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
}
TEST(CaptureContext) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"var f = (function () {"
"var x = 42;"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
// TODO(sigurds) For now we do not inline any native functions. If we do at
// some point, change this test.
TEST(DontInlineEval) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"var x = 42;"
"(function () {"
- "function bar(s, t) { return eval(\"AssertStackDepth(2); x\") };"
+ "function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
"return bar;"
"})();",
CompilationInfo::kInliningEnabled |
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
}
TEST(InlineOmitArguments) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 42;"
- "function bar(s, t, u, v) { AssertStackDepth(1); return x + s; };"
+ "function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
"return (function (s,t) { return bar(s); });"
"})();",
CompilationInfo::kInliningEnabled |
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
+TEST(InlineOmitArgumentsDeopt) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ "function foo(s,t,u,v) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
+ "return baz(); };"
+ "function bar() { return foo(11); };"
+ "function baz() { return foo.arguments.length == 1 && "
+ " foo.arguments[0] == 11 ; }"
+ "return bar;"
+ "})();",
+ CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
+
TEST(InlineSurplusArguments) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 42;"
- "function foo(s) { AssertStackDepth(1); return x + s; };"
+ "function foo(s) { AssertInlineCount(2); return x + s; };"
"function bar(s,t) { return foo(s,t,13); };"
"return bar;"
"})();",
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
+TEST(InlineSurplusArgumentsDeopt) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ "function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
+ "return baz(); };"
+ "function bar() { return foo(13, 14, 15); };"
+ "function baz() { return foo.arguments.length == 3 && "
+ " foo.arguments[0] == 13 && "
+ " foo.arguments[1] == 14 && "
+ " foo.arguments[2] == 15; }"
+ "return bar;"
+ "})();",
+ CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
+
TEST(InlineTwice) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 42;"
- "function bar(s) { AssertStackDepth(1); return x + s; };"
+ "function bar(s) { AssertInlineCount(2); return x + s; };"
"return (function (s,t) { return bar(s) + bar(t); });"
"})();",
CompilationInfo::kInliningEnabled |
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4));
}
TEST(InlineTwiceDependent) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 42;"
- "function foo(s) { AssertStackDepth(1); return x + s; };"
+ "function foo(s) { AssertInlineCount(2); return x + s; };"
"function bar(s,t) { return foo(foo(s)); };"
"return bar;"
"})();",
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4));
}
TEST(InlineTwiceDependentDiamond) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 41;"
- "function foo(s) { AssertStackDepth(1); if (s % 2 == 0) {"
+ "function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
" return x - s } else { return x + s; } };"
"function bar(s,t) { return foo(foo(s)); };"
"return bar;"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-11), T.Val(11), T.Val(4));
}
TEST(InlineTwiceDependentDiamondDifferent) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 41;"
- "function foo(s,t) { AssertStackDepth(1); if (s % 2 == 0) {"
+ "function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
" return x - s * t } else { return x + s * t; } };"
"function bar(s,t) { return foo(foo(s, 3), 5); };"
"return bar;"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-329), T.Val(11), T.Val(4));
}
TEST(InlineLoop) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = 41;"
- "function foo(s) { AssertStackDepth(1); while (s > 0) {"
+ "function foo(s) { AssertInlineCount(2); while (s > 0) {"
" s = s - 1; }; return s; };"
"function bar(s,t) { return foo(foo(s)); };"
"return bar;"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
}
TEST(InlineStrictIntoNonStrict) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = Object.create({}, { y: { value:42, writable:false } });"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckThrows(T.undefined(), T.undefined());
}
TEST(InlineNonStrictIntoStrict) {
+ FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
"var x = Object.create({}, { y: { value:42, writable:false } });"
CompilationInfo::kContextSpecializing |
CompilationInfo::kTypingEnabled);
- InstallAssertStackDepthHelper(CcTest::isolate());
+ InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.undefined(), T.undefined());
}