Commit c29a4560 authored by ishell's avatar ishell Committed by Commit bot

[turbofan] [deoptimizer] Support inlining of ES6 tail calls.

In case when F was called with incompatible number of arguments (and therefore
the arguments adator frame was created), F inlines a tail call of G which then
deopts the deoptimizer should also remove the arguments adaptor frame for F.

This CL adds required machinery to the deoptimizer.

BUG=v8:4698
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#34610}
parent e260bd53
...@@ -234,6 +234,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -234,6 +234,8 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ pop(r0); // Restore deoptimizer object (class Deoptimizer). __ pop(r0); // Restore deoptimizer object (class Deoptimizer).
__ ldr(sp, MemOperand(r0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames. // Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
......
...@@ -126,7 +126,7 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -126,7 +126,7 @@ void Deoptimizer::TableEntryGenerator::Generate() {
// address for lazy deoptimization. // address for lazy deoptimization.
__ Mov(code_object, lr); __ Mov(code_object, lr);
// Compute the fp-to-sp delta, and correct one word for bailout id. // Compute the fp-to-sp delta, and correct one word for bailout id.
__ Add(fp_to_sp, masm()->StackPointer(), __ Add(fp_to_sp, __ StackPointer(),
kSavedRegistersAreaSize + (1 * kPointerSize)); kSavedRegistersAreaSize + (1 * kPointerSize));
__ Sub(fp_to_sp, fp, fp_to_sp); __ Sub(fp_to_sp, fp, fp_to_sp);
...@@ -209,6 +209,9 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -209,6 +209,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ Pop(x4); // Restore deoptimizer object (class Deoptimizer). __ Pop(x4); // Restore deoptimizer object (class Deoptimizer).
__ Ldr(__ StackPointer(),
MemOperand(x4, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames. // Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
......
...@@ -629,6 +629,9 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor( ...@@ -629,6 +629,9 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
shared_info_id, shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count())); static_cast<unsigned int>(descriptor->parameters_count()));
break; break;
case FrameStateType::kTailCallerFunction:
translation->BeginTailCallerFrame(shared_info_id);
break;
case FrameStateType::kConstructStub: case FrameStateType::kConstructStub:
translation->BeginConstructStubFrame( translation->BeginConstructStubFrame(
shared_info_id, shared_info_id,
......
...@@ -58,6 +58,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) { ...@@ -58,6 +58,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kArgumentsAdaptor: case FrameStateType::kArgumentsAdaptor:
os << "ARGUMENTS_ADAPTOR"; os << "ARGUMENTS_ADAPTOR";
break; break;
case FrameStateType::kTailCallerFunction:
os << "TAIL_CALLER_FRAME";
break;
case FrameStateType::kConstructStub: case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB"; os << "CONSTRUCT_STUB";
break; break;
......
...@@ -79,10 +79,10 @@ enum class FrameStateType { ...@@ -79,10 +79,10 @@ enum class FrameStateType {
kJavaScriptFunction, // Represents an unoptimized JavaScriptFrame. kJavaScriptFunction, // Represents an unoptimized JavaScriptFrame.
kInterpretedFunction, // Represents an InterpretedFrame. kInterpretedFunction, // Represents an InterpretedFrame.
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame. kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kTailCallerFunction, // Represents a frame removed by tail call elimination.
kConstructStub // Represents a ConstructStubFrame. kConstructStub // Represents a ConstructStubFrame.
}; };
class FrameStateFunctionInfo { class FrameStateFunctionInfo {
public: public:
FrameStateFunctionInfo(FrameStateType type, int parameter_count, FrameStateFunctionInfo(FrameStateType type, int parameter_count,
......
...@@ -263,6 +263,35 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state, ...@@ -263,6 +263,35 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
node->InputAt(0), outer_frame_state); node->InputAt(0), outer_frame_state);
} }
Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) {
FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
Handle<SharedFunctionInfo> shared =
frame_info.shared_info().ToHandleChecked();
Node* function = frame_state->InputAt(kFrameStateFunctionInput);
// If we are inlining a tail call drop caller's frame state and an
// arguments adaptor if it exists.
frame_state = NodeProperties::GetFrameStateInput(frame_state, 0);
if (frame_state->opcode() == IrOpcode::kFrameState) {
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
if (state_info.type() == FrameStateType::kArgumentsAdaptor) {
frame_state = NodeProperties::GetFrameStateInput(frame_state, 0);
}
}
const FrameStateFunctionInfo* state_info =
jsgraph_->common()->CreateFrameStateFunctionInfo(
FrameStateType::kTailCallerFunction, 0, 0, shared);
const Operator* op = jsgraph_->common()->FrameState(
BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
const Operator* op0 = jsgraph_->common()->StateValues(0);
Node* node0 = jsgraph_->graph()->NewNode(op0);
return jsgraph_->graph()->NewNode(op, node0, node0, node0,
jsgraph_->UndefinedConstant(), function,
frame_state);
}
namespace { namespace {
...@@ -508,6 +537,20 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { ...@@ -508,6 +537,20 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
NodeProperties::ReplaceEffectInput(node, convert); NodeProperties::ReplaceEffectInput(node, convert);
} }
// If we are inlining a JS call at tail position then we have to pop current
// frame state and its potential arguments adaptor frame state in order to
// make the call stack be consistent with non-inlining case.
// After that we add a tail caller frame state which lets deoptimizer handle
// the case when the outermost function inlines a tail call (it should remove
// potential arguments adaptor frame that belongs to outermost function when
// deopt happens).
if (node->opcode() == IrOpcode::kJSCallFunction) {
const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
if (p.tail_call_mode() == TailCallMode::kAllow) {
frame_state = CreateTailCallerFrameState(node, frame_state);
}
}
// Insert argument adaptor frame if required. The callees formal parameter // Insert argument adaptor frame if required. The callees formal parameter
// count (i.e. value outputs of start node minus target, receiver, new target, // count (i.e. value outputs of start node minus target, receiver, new target,
// arguments count and context) have to match the number of arguments passed // arguments count and context) have to match the number of arguments passed
......
...@@ -45,6 +45,8 @@ class JSInliner final : public AdvancedReducer { ...@@ -45,6 +45,8 @@ class JSInliner final : public AdvancedReducer {
FrameStateType frame_state_type, FrameStateType frame_state_type,
Handle<SharedFunctionInfo> shared); Handle<SharedFunctionInfo> shared);
Node* CreateTailCallerFrameState(Node* node, Node* outer_frame_state);
Reduction InlineCall(Node* call, Node* new_target, Node* context, Reduction InlineCall(Node* call, Node* new_target, Node* context,
Node* frame_state, Node* start, Node* end); Node* frame_state, Node* start, Node* end);
}; };
......
This diff is collapsed.
...@@ -116,6 +116,7 @@ class TranslatedFrame { ...@@ -116,6 +116,7 @@ class TranslatedFrame {
kInterpretedFunction, kInterpretedFunction,
kGetter, kGetter,
kSetter, kSetter,
kTailCallerFunction,
kArgumentsAdaptor, kArgumentsAdaptor,
kConstructStub, kConstructStub,
kCompiledStub, kCompiledStub,
...@@ -186,6 +187,7 @@ class TranslatedFrame { ...@@ -186,6 +187,7 @@ class TranslatedFrame {
SharedFunctionInfo* shared_info); SharedFunctionInfo* shared_info);
static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo* shared_info, static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo* shared_info,
int height); int height);
static TranslatedFrame TailCallerFrame(SharedFunctionInfo* shared_info);
static TranslatedFrame ConstructStubFrame(SharedFunctionInfo* shared_info, static TranslatedFrame ConstructStubFrame(SharedFunctionInfo* shared_info,
int height); int height);
static TranslatedFrame CompiledStubFrame(int height, Isolate* isolate) { static TranslatedFrame CompiledStubFrame(int height, Isolate* isolate) {
...@@ -529,6 +531,10 @@ class Deoptimizer : public Malloced { ...@@ -529,6 +531,10 @@ class Deoptimizer : public Malloced {
} }
static int output_offset() { return OFFSET_OF(Deoptimizer, output_); } static int output_offset() { return OFFSET_OF(Deoptimizer, output_); }
static int caller_frame_top_offset() {
return OFFSET_OF(Deoptimizer, caller_frame_top_);
}
static int GetDeoptimizedCodeCount(Isolate* isolate); static int GetDeoptimizedCodeCount(Isolate* isolate);
static const int kNotDeoptimizationEntry = -1; static const int kNotDeoptimizationEntry = -1;
...@@ -582,12 +588,20 @@ class Deoptimizer : public Malloced { ...@@ -582,12 +588,20 @@ class Deoptimizer : public Malloced {
void DeleteFrameDescriptions(); void DeleteFrameDescriptions();
void DoComputeOutputFrames(); void DoComputeOutputFrames();
void DoComputeJSFrame(int frame_index, bool goto_catch_handler); void DoComputeJSFrame(TranslatedFrame* translated_frame, int frame_index,
void DoComputeInterpretedFrame(int frame_index, bool goto_catch_handler); bool goto_catch_handler);
void DoComputeArgumentsAdaptorFrame(int frame_index); void DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
void DoComputeConstructStubFrame(int frame_index); int frame_index, bool goto_catch_handler);
void DoComputeAccessorStubFrame(int frame_index, bool is_setter_stub_frame); void DoComputeArgumentsAdaptorFrame(TranslatedFrame* translated_frame,
void DoComputeCompiledStubFrame(int frame_index); int frame_index);
void DoComputeTailCallerFrame(TranslatedFrame* translated_frame,
int frame_index);
void DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
int frame_index);
void DoComputeAccessorStubFrame(TranslatedFrame* translated_frame,
int frame_index, bool is_setter_stub_frame);
void DoComputeCompiledStubFrame(TranslatedFrame* translated_frame,
int frame_index);
void WriteTranslatedValueToOutput( void WriteTranslatedValueToOutput(
TranslatedFrame::iterator* iterator, int* input_index, int frame_index, TranslatedFrame::iterator* iterator, int* input_index, int frame_index,
...@@ -935,6 +949,7 @@ class TranslationIterator BASE_EMBEDDED { ...@@ -935,6 +949,7 @@ class TranslationIterator BASE_EMBEDDED {
V(GETTER_STUB_FRAME) \ V(GETTER_STUB_FRAME) \
V(SETTER_STUB_FRAME) \ V(SETTER_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \ V(ARGUMENTS_ADAPTOR_FRAME) \
V(TAIL_CALLER_FRAME) \
V(COMPILED_STUB_FRAME) \ V(COMPILED_STUB_FRAME) \
V(DUPLICATED_OBJECT) \ V(DUPLICATED_OBJECT) \
V(ARGUMENTS_OBJECT) \ V(ARGUMENTS_OBJECT) \
...@@ -978,6 +993,7 @@ class Translation BASE_EMBEDDED { ...@@ -978,6 +993,7 @@ class Translation BASE_EMBEDDED {
unsigned height); unsigned height);
void BeginCompiledStubFrame(int height); void BeginCompiledStubFrame(int height);
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height); void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
void BeginTailCallerFrame(int literal_id);
void BeginConstructStubFrame(int literal_id, unsigned height); void BeginConstructStubFrame(int literal_id, unsigned height);
void BeginGetterStubFrame(int literal_id); void BeginGetterStubFrame(int literal_id);
void BeginSetterStubFrame(int literal_id); void BeginSetterStubFrame(int literal_id);
......
...@@ -302,7 +302,9 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -302,7 +302,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ pop(eax); __ pop(eax);
// Replace the current frame with the output frames. __ mov(esp, Operand(eax, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
// Outer loop state: eax = current FrameDescription**, edx = one past the // Outer loop state: eax = current FrameDescription**, edx = one past the
......
...@@ -238,6 +238,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -238,6 +238,8 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ pop(a0); // Restore deoptimizer object (class Deoptimizer). __ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ lw(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames. // Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
......
...@@ -237,6 +237,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -237,6 +237,8 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ pop(a0); // Restore deoptimizer object (class Deoptimizer). __ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ ld(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames. // Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
......
...@@ -862,6 +862,8 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT ...@@ -862,6 +862,8 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
if (has_initial_map()) os << Brief(initial_map()); if (has_initial_map()) os << Brief(initial_map());
os << "\n - shared_info = " << Brief(shared()); os << "\n - shared_info = " << Brief(shared());
os << "\n - name = " << Brief(shared()->name()); os << "\n - name = " << Brief(shared()->name());
os << "\n - formal_parameter_count = "
<< shared()->internal_formal_parameter_count();
if (shared()->is_generator()) { if (shared()->is_generator()) {
os << "\n - generator"; os << "\n - generator";
} }
...@@ -874,9 +876,10 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT ...@@ -874,9 +876,10 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "SharedFunctionInfo"); HeapObject::PrintHeader(os, "SharedFunctionInfo");
os << "\n - name: " << Brief(name()); os << "\n - name = " << Brief(name());
os << "\n - expected_nof_properties: " << expected_nof_properties(); os << "\n - formal_parameter_count = " << internal_formal_parameter_count();
os << "\n - ast_node_count: " << ast_node_count(); os << "\n - expected_nof_properties = " << expected_nof_properties();
os << "\n - ast_node_count = " << ast_node_count();
os << "\n - instance class name = "; os << "\n - instance class name = ";
instance_class_name()->Print(os); instance_class_name()->Print(os);
os << "\n - code = " << Brief(code()); os << "\n - code = " << Brief(code());
......
...@@ -14682,6 +14682,15 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( ...@@ -14682,6 +14682,15 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
break; break;
} }
case Translation::TAIL_CALLER_FRAME: {
int shared_info_id = iterator.Next();
Object* shared_info = LiteralArray()->get(shared_info_id);
os << "{function="
<< Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
<< "}";
break;
}
case Translation::GETTER_STUB_FRAME: case Translation::GETTER_STUB_FRAME:
case Translation::SETTER_STUB_FRAME: { case Translation::SETTER_STUB_FRAME: {
int shared_info_id = iterator.Next(); int shared_info_id = iterator.Next();
......
...@@ -122,10 +122,6 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { ...@@ -122,10 +122,6 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
deoptimizer->MaterializeHeapObjects(&it); deoptimizer->MaterializeHeapObjects(&it);
delete deoptimizer; delete deoptimizer;
JavaScriptFrame* frame = it.frame();
RUNTIME_ASSERT(frame->function()->IsJSFunction());
DCHECK(frame->function() == *function);
// Ensure the context register is updated for materialized objects. // Ensure the context register is updated for materialized objects.
JavaScriptFrameIterator top_it(isolate); JavaScriptFrameIterator top_it(isolate);
JavaScriptFrame* top_frame = top_it.frame(); JavaScriptFrame* top_frame = top_it.frame();
...@@ -135,7 +131,10 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { ...@@ -135,7 +131,10 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
// Search for other activations of the same function and code. // Search for other activations of the same optimized code.
// At this point {it} is at the topmost frame of all the frames materialized
// by the deoptimizer. Note that this frame does not necessarily represent
// an activation of {function} because of potential inlined tail-calls.
ActivationsFinder activations_finder(*optimized_code); ActivationsFinder activations_finder(*optimized_code);
activations_finder.VisitFrames(&it); activations_finder.VisitFrames(&it);
isolate->thread_manager()->IterateArchivedThreads(&activations_finder); isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
......
...@@ -231,7 +231,9 @@ void Deoptimizer::TableEntryGenerator::Generate() { ...@@ -231,7 +231,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
} }
__ popq(rax); __ popq(rax);
// Replace the current frame with the output frames. __ movp(rsp, Operand(rax, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
// Outer loop state: rax = current FrameDescription**, rdx = one past the // Outer loop state: rax = current FrameDescription**, rdx = one past the
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls --no-turbo-inlining // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases. // TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --nostress-opt // Flags: --turbo --nostress-opt
Error.prepareStackTrace = (error,stack) => { Error.prepareStackTrace = (error,stack) => {
...@@ -306,11 +306,10 @@ function run_tests() { ...@@ -306,11 +306,10 @@ function run_tests() {
return source; return source;
} }
// TODO(v8:4698), TODO(ishell): support all commented cases.
var f_args_variants = ["", "1", "1, 2"]; var f_args_variants = ["", "1", "1, 2"];
var g_args_variants = ["", "10", "10, 20"]; var g_args_variants = ["", "10", "10, 20"];
var f_inlinable_variants = [/*true,*/ false]; var f_inlinable_variants = [true, false];
var g_inlinable_variants = [/*true,*/ false]; var g_inlinable_variants = [true, false];
var f_variants = [ var f_variants = [
f_cfg_sloppy, f_cfg_sloppy,
f_cfg_strict, f_cfg_strict,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment