[crankshaft] Support inlining of function calls in tail position (in ES6 sense).

BUG=v8:4698
LOG=N
TBR=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/1782743003

Cr-Commit-Position: refs/heads/master@{#34992}
This commit is contained in:
ishell 2016-03-22 07:43:37 -07:00 committed by Commit bot
parent 9383d14b9f
commit df694d5524
14 changed files with 136 additions and 89 deletions

View File

@ -2498,11 +2498,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -1425,11 +1425,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if ((instr->arguments_var() != NULL) &&
instr->arguments_object()->IsLinked()) {

View File

@ -1939,10 +1939,12 @@ class HEnterInlined final : public HTemplateInstruction<0> {
HConstant* closure_context, int arguments_count,
FunctionLiteral* function,
InliningKind inlining_kind, Variable* arguments_var,
HArgumentsObject* arguments_object) {
return new (zone) HEnterInlined(return_id, closure, closure_context,
arguments_count, function, inlining_kind,
arguments_var, arguments_object, zone);
HArgumentsObject* arguments_object,
TailCallMode syntactic_tail_call_mode) {
return new (zone)
HEnterInlined(return_id, closure, closure_context, arguments_count,
function, inlining_kind, arguments_var, arguments_object,
syntactic_tail_call_mode, zone);
}
void RegisterReturnTarget(HBasicBlock* return_target, Zone* zone);
@ -1958,6 +1960,9 @@ class HEnterInlined final : public HTemplateInstruction<0> {
void set_arguments_pushed() { arguments_pushed_ = true; }
FunctionLiteral* function() const { return function_; }
InliningKind inlining_kind() const { return inlining_kind_; }
TailCallMode syntactic_tail_call_mode() const {
return syntactic_tail_call_mode_;
}
BailoutId ReturnId() const { return return_id_; }
int inlining_id() const { return inlining_id_; }
void set_inlining_id(int inlining_id) { inlining_id_ = inlining_id; }
@ -1976,7 +1981,7 @@ class HEnterInlined final : public HTemplateInstruction<0> {
HConstant* closure_context, int arguments_count,
FunctionLiteral* function, InliningKind inlining_kind,
Variable* arguments_var, HArgumentsObject* arguments_object,
Zone* zone)
TailCallMode syntactic_tail_call_mode, Zone* zone)
: return_id_(return_id),
shared_(handle(closure->shared())),
closure_(closure),
@ -1985,6 +1990,7 @@ class HEnterInlined final : public HTemplateInstruction<0> {
arguments_pushed_(false),
function_(function),
inlining_kind_(inlining_kind),
syntactic_tail_call_mode_(syntactic_tail_call_mode),
inlining_id_(0),
arguments_var_(arguments_var),
arguments_object_(arguments_object),
@ -1998,6 +2004,7 @@ class HEnterInlined final : public HTemplateInstruction<0> {
bool arguments_pushed_;
FunctionLiteral* function_;
InliningKind inlining_kind_;
TailCallMode syntactic_tail_call_mode_;
int inlining_id_;
Variable* arguments_var_;
HArgumentsObject* arguments_object_;

View File

@ -4114,7 +4114,7 @@ AstContext::AstContext(HOptimizedGraphBuilder* owner, Expression::Context kind)
typeof_mode_(NOT_INSIDE_TYPEOF) {
owner->set_ast_context(this); // Push.
#ifdef DEBUG
DCHECK(owner->environment()->frame_type() == JS_FUNCTION);
DCHECK_EQ(JS_FUNCTION, owner->environment()->frame_type());
original_length_ = owner->environment()->length();
#endif
}
@ -4126,18 +4126,18 @@ AstContext::~AstContext() {
EffectContext::~EffectContext() {
DCHECK(owner()->HasStackOverflow() ||
owner()->current_block() == NULL ||
DCHECK(owner()->HasStackOverflow() || owner()->current_block() == NULL ||
(owner()->environment()->length() == original_length_ &&
owner()->environment()->frame_type() == JS_FUNCTION));
(owner()->environment()->frame_type() == JS_FUNCTION ||
owner()->environment()->frame_type() == TAIL_CALLER_FUNCTION)));
}
ValueContext::~ValueContext() {
DCHECK(owner()->HasStackOverflow() ||
owner()->current_block() == NULL ||
DCHECK(owner()->HasStackOverflow() || owner()->current_block() == NULL ||
(owner()->environment()->length() == original_length_ + 1 &&
owner()->environment()->frame_type() == JS_FUNCTION));
(owner()->environment()->frame_type() == JS_FUNCTION ||
owner()->environment()->frame_type() == TAIL_CALLER_FUNCTION)));
}
@ -8368,11 +8368,6 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
if (nodes_added == kNotInlinable) return false;
Handle<JSFunction> caller = current_info()->closure();
if (syntactic_tail_call_mode == TailCallMode::kAllow) {
TraceInline(target, caller, "call is at tail position");
return false;
}
if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
TraceInline(target, caller, "target AST is too large [early]");
return false;
@ -8530,12 +8525,9 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner_env =
environment()->CopyForInlining(target,
arguments_count,
function,
undefined,
function_state()->inlining_kind());
HEnvironment* inner_env = environment()->CopyForInlining(
target, arguments_count, function, undefined,
function_state()->inlining_kind(), syntactic_tail_call_mode);
HConstant* context = Add<HConstant>(Handle<Context>(target->context()));
inner_env->BindContext(context);
@ -8565,10 +8557,10 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
current_block()->UpdateEnvironment(inner_env);
Scope* saved_scope = scope();
set_scope(target_info.scope());
HEnterInlined* enter_inlined =
Add<HEnterInlined>(return_id, target, context, arguments_count, function,
function_state()->inlining_kind(),
function->scope()->arguments(), arguments_object);
HEnterInlined* enter_inlined = Add<HEnterInlined>(
return_id, target, context, arguments_count, function,
function_state()->inlining_kind(), function->scope()->arguments(),
arguments_object, syntactic_tail_call_mode);
if (top_info()->is_tracking_positions()) {
enter_inlined->set_inlining_id(inlining_id);
}
@ -9254,9 +9246,6 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(
top_info()->closure()->context()->native_context()) {
return false;
}
if (syntactic_tail_call_mode == TailCallMode::kAllow) {
return false;
}
if (argc > CallApiCallbackStub::kArgMax) {
return false;
}
@ -9364,14 +9353,16 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(
HConstant* code_value = Add<HConstant>(code);
call = New<HCallWithDescriptor>(
code_value, argc + 1, stub.GetCallInterfaceDescriptor(),
Vector<HValue*>(op_vals, arraysize(op_vals) - 1));
Vector<HValue*>(op_vals, arraysize(op_vals) - 1),
syntactic_tail_call_mode);
} else {
CallApiCallbackStub stub(isolate(), argc, call_data_undefined);
Handle<Code> code = stub.GetCode();
HConstant* code_value = Add<HConstant>(code);
call = New<HCallWithDescriptor>(
code_value, argc + 1, stub.GetCallInterfaceDescriptor(),
Vector<HValue*>(op_vals, arraysize(op_vals) - 1));
Vector<HValue*>(op_vals, arraysize(op_vals) - 1),
syntactic_tail_call_mode);
Drop(1); // Drop function.
}
@ -13197,14 +13188,16 @@ HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
return new_env;
}
void HEnvironment::MarkAsTailCaller() {
DCHECK_EQ(JS_FUNCTION, frame_type());
frame_type_ = TAIL_CALLER_FUNCTION;
}
HEnvironment* HEnvironment::CopyForInlining(
Handle<JSFunction> target,
int arguments,
FunctionLiteral* function,
HConstant* undefined,
InliningKind inlining_kind) const {
DCHECK(frame_type() == JS_FUNCTION);
Handle<JSFunction> target, int arguments, FunctionLiteral* function,
HConstant* undefined, InliningKind inlining_kind,
TailCallMode syntactic_tail_call_mode) const {
DCHECK_EQ(JS_FUNCTION, frame_type());
// Outer environment is a copy of this one without the arguments.
int arity = function->scope()->num_parameters();
@ -13213,6 +13206,11 @@ HEnvironment* HEnvironment::CopyForInlining(
outer->Drop(arguments + 1); // Including receiver.
outer->ClearHistory();
if (syntactic_tail_call_mode == TailCallMode::kAllow) {
DCHECK_EQ(NORMAL_RETURN, inlining_kind);
outer->MarkAsTailCaller();
}
if (inlining_kind == CONSTRUCT_CALL_RETURN) {
// Create artificial constructor stub environment. The receiver should
// actually be the constructor function, but we pass the newly allocated

View File

@ -501,10 +501,10 @@ enum FrameType {
JS_GETTER,
JS_SETTER,
ARGUMENTS_ADAPTOR,
TAIL_CALLER_FUNCTION,
STUB
};
class HEnvironment final : public ZoneObject {
public:
HEnvironment(HEnvironment* outer,
@ -613,15 +613,17 @@ class HEnvironment final : public ZoneObject {
// Create an "inlined version" of this environment, where the original
// environment is the outer environment but the top expression stack
// elements are moved to an inner environment as parameters.
HEnvironment* CopyForInlining(Handle<JSFunction> target,
int arguments,
FunctionLiteral* function,
HConstant* undefined,
InliningKind inlining_kind) const;
HEnvironment* CopyForInlining(Handle<JSFunction> target, int arguments,
FunctionLiteral* function, HConstant* undefined,
InliningKind inlining_kind,
TailCallMode syntactic_tail_call_mode) const;
HEnvironment* DiscardInlined(bool drop_extra) {
HEnvironment* outer = outer_;
while (outer->frame_type() != JS_FUNCTION) outer = outer->outer_;
while (outer->frame_type() != JS_FUNCTION &&
outer->frame_type() != TAIL_CALLER_FUNCTION) {
outer = outer->outer_;
}
if (drop_extra) outer->Drop(1);
return outer;
}
@ -680,6 +682,10 @@ class HEnvironment final : public ZoneObject {
FrameType frame_type,
int arguments) const;
// Marks current environment as tail caller by setting frame type to
// TAIL_CALLER_FUNCTION.
void MarkAsTailCaller();
// True if index is included in the expression stack part of the environment.
bool HasExpressionAt(int index) const;
@ -1278,6 +1284,26 @@ class HGraphBuilder {
return AddInstructionTyped(New<I>(p1, p2, p3, p4, p5, p6, p7, p8));
}
template <class I, class P1, class P2, class P3, class P4, class P5, class P6,
class P7, class P8, class P9>
I* New(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) {
return I::New(isolate(), zone(), context(), p1, p2, p3, p4, p5, p6, p7, p8,
p9);
}
template <class I, class P1, class P2, class P3, class P4, class P5, class P6,
class P7, class P8, class P9>
HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7,
P8 p8, P9 p9) {
return AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7, p8, p8));
}
template <class I, class P1, class P2, class P3, class P4, class P5, class P6,
class P7, class P8, class P9>
I* Add(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) {
return AddInstructionTyped(New<I>(p1, p2, p3, p4, p5, p6, p7, p8, p9));
}
void AddSimulate(BailoutId id, RemovableSimulate removable = FIXED_SIMULATE);
// When initializing arrays, we'll unfold the loop if the number of elements

View File

@ -2551,11 +2551,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -240,8 +240,8 @@ void LCodeGenBase::WriteTranslationFrame(LEnvironment* environment,
break;
}
case JS_GETTER: {
DCHECK(translation_size == 1);
DCHECK(height == 0);
DCHECK_EQ(1, translation_size);
DCHECK_EQ(0, height);
int shared_id = DefineDeoptimizationLiteral(
environment->entry() ? environment->entry()->shared()
: info()->shared_info());
@ -255,8 +255,8 @@ void LCodeGenBase::WriteTranslationFrame(LEnvironment* environment,
break;
}
case JS_SETTER: {
DCHECK(translation_size == 2);
DCHECK(height == 0);
DCHECK_EQ(2, translation_size);
DCHECK_EQ(0, height);
int shared_id = DefineDeoptimizationLiteral(
environment->entry() ? environment->entry()->shared()
: info()->shared_info());
@ -269,6 +269,20 @@ void LCodeGenBase::WriteTranslationFrame(LEnvironment* environment,
}
break;
}
case TAIL_CALLER_FUNCTION: {
DCHECK_EQ(0, translation_size);
int shared_id = DefineDeoptimizationLiteral(
environment->entry() ? environment->entry()->shared()
: info()->shared_info());
translation->BeginTailCallerFrame(shared_id);
if (info()->closure().is_identical_to(environment->closure())) {
translation->StoreJSFrameFunction();
} else {
int closure_id = DefineDeoptimizationLiteral(environment->closure());
translation->StoreLiteral(closure_id);
}
break;
}
case ARGUMENTS_ADAPTOR: {
int shared_id = DefineDeoptimizationLiteral(
environment->entry() ? environment->entry()->shared()

View File

@ -522,12 +522,28 @@ LEnvironment* LChunkBuilderBase::CreateEnvironment(
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
BailoutId ast_id = hydrogen_env->ast_id();
DCHECK(!ast_id.IsNone() ||
(hydrogen_env->frame_type() != JS_FUNCTION &&
hydrogen_env->frame_type() != TAIL_CALLER_FUNCTION));
if (hydrogen_env->frame_type() == TAIL_CALLER_FUNCTION) {
// Skip potential outer arguments adaptor frame.
HEnvironment* outer_hydrogen_env = hydrogen_env->outer();
if (outer_hydrogen_env != nullptr &&
outer_hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
outer_hydrogen_env = outer_hydrogen_env->outer();
}
LEnvironment* outer = CreateEnvironment(
outer_hydrogen_env, argument_index_accumulator, objects_to_materialize);
return new (zone())
LEnvironment(hydrogen_env->closure(), hydrogen_env->frame_type(),
ast_id, 0, 0, 0, outer, hydrogen_env->entry(), zone());
}
LEnvironment* outer =
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
DCHECK(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int omitted_count = (hydrogen_env->frame_type() == JS_FUNCTION)
? 0

View File

@ -2445,11 +2445,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -2450,11 +2450,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -2430,7 +2430,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind());
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -2236,7 +2236,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind());
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -2553,11 +2553,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());

View File

@ -2556,11 +2556,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HEnvironment* outer = current_block_->last_environment();
outer->set_ast_id(instr->ReturnId());
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->arguments_count(),
instr->function(),
undefined,
instr->inlining_kind());
HEnvironment* inner = outer->CopyForInlining(
instr->closure(), instr->arguments_count(), instr->function(), undefined,
instr->inlining_kind(), instr->syntactic_tail_call_mode());
// Only replay binding of arguments object if it wasn't removed from graph.
if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
inner->Bind(instr->arguments_var(), instr->arguments_object());