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() {
}
__ 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.
Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header;
......
......@@ -126,7 +126,7 @@ void Deoptimizer::TableEntryGenerator::Generate() {
// address for lazy deoptimization.
__ Mov(code_object, lr);
// 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));
__ Sub(fp_to_sp, fp, fp_to_sp);
......@@ -209,6 +209,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
}
__ 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.
Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header;
......
......@@ -629,6 +629,9 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count()));
break;
case FrameStateType::kTailCallerFunction:
translation->BeginTailCallerFrame(shared_info_id);
break;
case FrameStateType::kConstructStub:
translation->BeginConstructStubFrame(
shared_info_id,
......
......@@ -58,6 +58,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kArgumentsAdaptor:
os << "ARGUMENTS_ADAPTOR";
break;
case FrameStateType::kTailCallerFunction:
os << "TAIL_CALLER_FRAME";
break;
case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB";
break;
......
......@@ -79,10 +79,10 @@ enum class FrameStateType {
kJavaScriptFunction, // Represents an unoptimized JavaScriptFrame.
kInterpretedFunction, // Represents an InterpretedFrame.
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kTailCallerFunction, // Represents a frame removed by tail call elimination.
kConstructStub // Represents a ConstructStubFrame.
};
class FrameStateFunctionInfo {
public:
FrameStateFunctionInfo(FrameStateType type, int parameter_count,
......
......@@ -263,6 +263,35 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* 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 {
......@@ -508,6 +537,20 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
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
// 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
......
......@@ -45,6 +45,8 @@ class JSInliner final : public AdvancedReducer {
FrameStateType frame_state_type,
Handle<SharedFunctionInfo> shared);
Node* CreateTailCallerFrameState(Node* node, Node* outer_frame_state);
Reduction InlineCall(Node* call, Node* new_target, Node* context,
Node* frame_state, Node* start, Node* end);
};
......
This diff is collapsed.
......@@ -116,6 +116,7 @@ class TranslatedFrame {
kInterpretedFunction,
kGetter,
kSetter,
kTailCallerFunction,
kArgumentsAdaptor,
kConstructStub,
kCompiledStub,
......@@ -186,6 +187,7 @@ class TranslatedFrame {
SharedFunctionInfo* shared_info);
static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo* shared_info,
int height);
static TranslatedFrame TailCallerFrame(SharedFunctionInfo* shared_info);
static TranslatedFrame ConstructStubFrame(SharedFunctionInfo* shared_info,
int height);
static TranslatedFrame CompiledStubFrame(int height, Isolate* isolate) {
......@@ -529,6 +531,10 @@ class Deoptimizer : public Malloced {
}
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 const int kNotDeoptimizationEntry = -1;
......@@ -582,12 +588,20 @@ class Deoptimizer : public Malloced {
void DeleteFrameDescriptions();
void DoComputeOutputFrames();
void DoComputeJSFrame(int frame_index, bool goto_catch_handler);
void DoComputeInterpretedFrame(int frame_index, bool goto_catch_handler);
void DoComputeArgumentsAdaptorFrame(int frame_index);
void DoComputeConstructStubFrame(int frame_index);
void DoComputeAccessorStubFrame(int frame_index, bool is_setter_stub_frame);
void DoComputeCompiledStubFrame(int frame_index);
void DoComputeJSFrame(TranslatedFrame* translated_frame, int frame_index,
bool goto_catch_handler);
void DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
int frame_index, bool goto_catch_handler);
void DoComputeArgumentsAdaptorFrame(TranslatedFrame* translated_frame,
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(
TranslatedFrame::iterator* iterator, int* input_index, int frame_index,
......@@ -935,6 +949,7 @@ class TranslationIterator BASE_EMBEDDED {
V(GETTER_STUB_FRAME) \
V(SETTER_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \
V(TAIL_CALLER_FRAME) \
V(COMPILED_STUB_FRAME) \
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_OBJECT) \
......@@ -978,6 +993,7 @@ class Translation BASE_EMBEDDED {
unsigned height);
void BeginCompiledStubFrame(int height);
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
void BeginTailCallerFrame(int literal_id);
void BeginConstructStubFrame(int literal_id, unsigned height);
void BeginGetterStubFrame(int literal_id);
void BeginSetterStubFrame(int literal_id);
......
......@@ -302,7 +302,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
}
__ 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,
outer_loop_header, inner_loop_header;
// Outer loop state: eax = current FrameDescription**, edx = one past the
......
......@@ -238,6 +238,8 @@ void Deoptimizer::TableEntryGenerator::Generate() {
}
__ 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.
Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header;
......
......@@ -237,6 +237,8 @@ void Deoptimizer::TableEntryGenerator::Generate() {
}
__ 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.
Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header;
......
......@@ -862,6 +862,8 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
if (has_initial_map()) os << Brief(initial_map());
os << "\n - shared_info = " << Brief(shared());
os << "\n - name = " << Brief(shared()->name());
os << "\n - formal_parameter_count = "
<< shared()->internal_formal_parameter_count();
if (shared()->is_generator()) {
os << "\n - generator";
}
......@@ -874,9 +876,10 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "SharedFunctionInfo");
os << "\n - name: " << Brief(name());
os << "\n - expected_nof_properties: " << expected_nof_properties();
os << "\n - ast_node_count: " << ast_node_count();
os << "\n - name = " << Brief(name());
os << "\n - formal_parameter_count = " << internal_formal_parameter_count();
os << "\n - expected_nof_properties = " << expected_nof_properties();
os << "\n - ast_node_count = " << ast_node_count();
os << "\n - instance class name = ";
instance_class_name()->Print(os);
os << "\n - code = " << Brief(code());
......
......@@ -14682,6 +14682,15 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
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::SETTER_STUB_FRAME: {
int shared_info_id = iterator.Next();
......
......@@ -122,10 +122,6 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
deoptimizer->MaterializeHeapObjects(&it);
delete deoptimizer;
JavaScriptFrame* frame = it.frame();
RUNTIME_ASSERT(frame->function()->IsJSFunction());
DCHECK(frame->function() == *function);
// Ensure the context register is updated for materialized objects.
JavaScriptFrameIterator top_it(isolate);
JavaScriptFrame* top_frame = top_it.frame();
......@@ -135,7 +131,10 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
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);
activations_finder.VisitFrames(&it);
isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
......
......@@ -231,7 +231,9 @@ void Deoptimizer::TableEntryGenerator::Generate() {
}
__ 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,
outer_loop_header, inner_loop_header;
// Outer loop state: rax = current FrameDescription**, rdx = one past the
......
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// 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.
// Flags: --nostress-opt
// Flags: --turbo --nostress-opt
Error.prepareStackTrace = (error,stack) => {
......@@ -306,11 +306,10 @@ function run_tests() {
return source;
}
// TODO(v8:4698), TODO(ishell): support all commented cases.
var f_args_variants = ["", "1", "1, 2"];
var g_args_variants = ["", "10", "10, 20"];
var f_inlinable_variants = [/*true,*/ false];
var g_inlinable_variants = [/*true,*/ false];
var f_inlinable_variants = [true, false];
var g_inlinable_variants = [true, false];
var f_variants = [
f_cfg_sloppy,
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