Commit 32b4bc13 authored by ishell's avatar ishell Committed by Commit bot

[es6] [interpreter] Add tail calls support to Ignition.

This CL introduces two new bytecodes TailCall and TailCallWide.

BUG=v8:4698,v8:4687
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#34083}
parent c8117f2f
......@@ -1103,7 +1103,8 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm, Register index,
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- r0 : the number of arguments (not including the receiver)
// -- r2 : the address of the first argument to be pushed. Subsequent
......@@ -1121,7 +1122,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
Generate_InterpreterPushArgs(masm, r2, r3, r4);
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......@@ -2065,6 +2068,16 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
__ cmp(scratch1, Operand(0));
__ b(ne, &done);
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ ldr(scratch3, MemOperand(fp, StandardFrameConstants::kMarkerOffset));
__ cmp(scratch3, Operand(Smi::FromInt(StackFrame::STUB)));
__ b(ne, &no_interpreter_frame);
__ ldr(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
__ ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
......
......@@ -2098,6 +2098,16 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
__ Cmp(scratch1, Operand(0));
__ B(ne, &done);
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ Ldr(scratch3, MemOperand(fp, StandardFrameConstants::kMarkerOffset));
__ Cmp(scratch3, Operand(Smi::FromInt(StackFrame::STUB)));
__ B(ne, &no_interpreter_frame);
__ Ldr(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
__ Ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
......@@ -2573,7 +2583,8 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver)
// -- x2 : the address of the first argument to be pushed. Subsequent
......@@ -2601,7 +2612,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ B(gt, &loop_header);
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......
......@@ -3989,6 +3989,16 @@ Handle<Code> Builtins::CallBoundFunction(TailCallMode tail_call_mode) {
return Handle<Code>::null();
}
Handle<Code> Builtins::InterpreterPushArgsAndCall(TailCallMode tail_call_mode) {
switch (tail_call_mode) {
case TailCallMode::kDisallow:
return InterpreterPushArgsAndCall();
case TailCallMode::kAllow:
return InterpreterPushArgsAndTailCall();
}
UNREACHABLE();
return Handle<Code>::null();
}
namespace {
......
......@@ -214,6 +214,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndTailCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterNotifyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterNotifySoftDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
......@@ -362,6 +363,7 @@ class Builtins {
Handle<Code> Call(ConvertReceiverMode = ConvertReceiverMode::kAny,
TailCallMode tail_call_mode = TailCallMode::kDisallow);
Handle<Code> CallBoundFunction(TailCallMode tail_call_mode);
Handle<Code> InterpreterPushArgsAndCall(TailCallMode tail_call_mode);
Code* builtin(Name name) {
// Code::cast cannot be used here since we access builtins
......@@ -577,7 +579,15 @@ class Builtins {
static void Generate_InterpreterEntryTrampoline(MacroAssembler* masm);
static void Generate_InterpreterExitTrampoline(MacroAssembler* masm);
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm);
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
return Generate_InterpreterPushArgsAndCallImpl(masm,
TailCallMode::kDisallow);
}
static void Generate_InterpreterPushArgsAndTailCall(MacroAssembler* masm) {
return Generate_InterpreterPushArgsAndCallImpl(masm, TailCallMode::kAllow);
}
static void Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode);
static void Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm);
static void Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm);
static void Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm);
......
......@@ -347,9 +347,11 @@ Callable CodeFactory::ConstructFunction(Isolate* isolate) {
// static
Callable CodeFactory::InterpreterPushArgsAndCall(Isolate* isolate) {
return Callable(isolate->builtins()->InterpreterPushArgsAndCall(),
InterpreterPushArgsAndCallDescriptor(isolate));
Callable CodeFactory::InterpreterPushArgsAndCall(Isolate* isolate,
TailCallMode tail_call_mode) {
return Callable(
isolate->builtins()->InterpreterPushArgsAndCall(tail_call_mode),
InterpreterPushArgsAndCallDescriptor(isolate));
}
......
......@@ -105,7 +105,8 @@ class CodeFactory final {
static Callable Construct(Isolate* isolate);
static Callable ConstructFunction(Isolate* isolate);
static Callable InterpreterPushArgsAndCall(Isolate* isolate);
static Callable InterpreterPushArgsAndCall(Isolate* isolate,
TailCallMode tail_call_mode);
static Callable InterpreterPushArgsAndConstruct(Isolate* isolate);
static Callable InterpreterCEntry(Isolate* isolate, int result_size = 1);
};
......
......@@ -993,7 +993,7 @@ Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
return value;
}
void BytecodeGraphBuilder::BuildCall() {
void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode) {
FrameStateBeforeAndAfter states(this);
// TODO(rmcilroy): Set receiver_hint correctly based on whether the receiver
// register has been loaded with null / undefined explicitly or we are sure it
......@@ -1006,16 +1006,23 @@ void BytecodeGraphBuilder::BuildCall() {
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(3));
// TODO(ishell): provide correct tail_call_mode value to CallFunction.
const Operator* call =
javascript()->CallFunction(arg_count + 1, feedback, receiver_hint);
const Operator* call = javascript()->CallFunction(
arg_count + 1, feedback, receiver_hint, tail_call_mode);
Node* value = ProcessCallArguments(call, callee, receiver, arg_count + 1);
environment()->BindAccumulator(value, &states);
}
void BytecodeGraphBuilder::VisitCall() { BuildCall(); }
void BytecodeGraphBuilder::VisitCall() { BuildCall(TailCallMode::kDisallow); }
void BytecodeGraphBuilder::VisitCallWide() { BuildCall(); }
void BytecodeGraphBuilder::VisitCallWide() {
BuildCall(TailCallMode::kDisallow);
}
void BytecodeGraphBuilder::VisitTailCall() { BuildCall(TailCallMode::kAllow); }
void BytecodeGraphBuilder::VisitTailCallWide() {
BuildCall(TailCallMode::kAllow);
}
void BytecodeGraphBuilder::BuildCallJSRuntime() {
FrameStateBeforeAndAfter states(this);
......
......@@ -123,7 +123,7 @@ class BytecodeGraphBuilder {
void BuildKeyedStore(LanguageMode language_mode);
void BuildLdaLookupSlot(TypeofMode typeof_mode);
void BuildStaLookupSlot(LanguageMode language_mode);
void BuildCall();
void BuildCall(TailCallMode tail_call_mode);
void BuildCallJSRuntime();
void BuildCallRuntime();
void BuildCallRuntimeForPair();
......
......@@ -676,7 +676,8 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- ebx : the address of the first argument to be pushed. Subsequent
......@@ -699,7 +700,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// Call the target.
__ Push(edx); // Re-push return address.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......@@ -1989,6 +1992,16 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
__ cmp(scratch1, Immediate(0));
__ j(not_equal, &done, Label::kNear);
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ cmp(Operand(ebp, StandardFrameConstants::kMarkerOffset),
Immediate(Smi::FromInt(StackFrame::STUB)));
__ j(not_equal, &no_interpreter_frame, Label::kNear);
__ mov(ebp, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
__ mov(scratch2, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
......
......@@ -1062,20 +1062,21 @@ void BytecodeArrayBuilder::EnsureReturn(FunctionLiteral* literal) {
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver_args,
size_t receiver_args_count,
int feedback_slot) {
int feedback_slot,
TailCallMode tail_call_mode) {
Bytecode bytecode = BytecodeForCall(tail_call_mode);
if (FitsInReg8Operand(callable) && FitsInReg8Operand(receiver_args) &&
FitsInIdx8Operand(receiver_args_count) &&
FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kCall, callable.ToRawOperand(),
receiver_args.ToRawOperand(),
Output(bytecode, callable.ToRawOperand(), receiver_args.ToRawOperand(),
static_cast<uint8_t>(receiver_args_count),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInReg16Operand(callable) &&
FitsInReg16Operand(receiver_args) &&
FitsInIdx16Operand(receiver_args_count) &&
FitsInIdx16Operand(feedback_slot)) {
Output(Bytecode::kCallWide, callable.ToRawOperand(),
receiver_args.ToRawOperand(),
bytecode = BytecodeForWideOperands(bytecode);
Output(bytecode, callable.ToRawOperand(), receiver_args.ToRawOperand(),
static_cast<uint16_t>(receiver_args_count),
static_cast<uint16_t>(feedback_slot));
} else {
......@@ -1425,6 +1426,10 @@ Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
// static
Bytecode BytecodeArrayBuilder::BytecodeForWideOperands(Bytecode bytecode) {
switch (bytecode) {
case Bytecode::kCall:
return Bytecode::kCallWide;
case Bytecode::kTailCall:
return Bytecode::kTailCallWide;
case Bytecode::kLoadIC:
return Bytecode::kLoadICWide;
case Bytecode::kKeyedLoadIC:
......@@ -1564,6 +1569,18 @@ Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCall(TailCallMode tail_call_mode) {
switch (tail_call_mode) {
case TailCallMode::kDisallow:
return Bytecode::kCall;
case TailCallMode::kAllow:
return Bytecode::kTailCall;
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
......
......@@ -161,8 +161,15 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
// |callable|, the receiver should be in |receiver_args| and all subsequent
// arguments should be in registers <receiver_args + 1> to
// <receiver_args + receiver_arg_count - 1>.
BytecodeArrayBuilder& Call(Register callable, Register receiver_args,
size_t receiver_arg_count, int feedback_slot);
BytecodeArrayBuilder& Call(
Register callable, Register receiver_args, size_t receiver_arg_count,
int feedback_slot, TailCallMode tail_call_mode = TailCallMode::kDisallow);
BytecodeArrayBuilder& TailCall(Register callable, Register receiver_args,
size_t receiver_arg_count, int feedback_slot) {
return Call(callable, receiver_args, receiver_arg_count, feedback_slot,
TailCallMode::kAllow);
}
// Call the new operator. The accumulator holds the |new_target|.
// The |constructor| is in a register followed by |arg_count|
......@@ -280,6 +287,7 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
static Bytecode BytecodeForStoreLookupSlot(LanguageMode language_mode);
static Bytecode BytecodeForCreateArguments(CreateArgumentsType type);
static Bytecode BytecodeForDelete(LanguageMode language_mode);
static Bytecode BytecodeForCall(TailCallMode tail_call_mode);
static bool FitsInIdx8Operand(int value);
static bool FitsInIdx8Operand(size_t value);
......
......@@ -2462,7 +2462,8 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->SetExpressionPosition(expr);
builder()->Call(callee, receiver, 1 + args->length(),
feedback_index(expr->CallFeedbackICSlot()));
feedback_index(expr->CallFeedbackICSlot()),
expr->tail_call_mode());
execution_result()->SetResultInAccumulator();
}
......
......@@ -262,8 +262,9 @@ bool Bytecodes::IsJump(Bytecode bytecode) {
// static
bool Bytecodes::IsCallOrNew(Bytecode bytecode) {
return bytecode == Bytecode::kCall || bytecode == Bytecode::kNew ||
bytecode == Bytecode::kCallWide || bytecode == Bytecode::kNewWide;
return bytecode == Bytecode::kCall || bytecode == Bytecode::kTailCall ||
bytecode == Bytecode::kNew || bytecode == Bytecode::kCallWide ||
bytecode == Bytecode::kTailCallWide || bytecode == Bytecode::kNewWide;
}
// static
......
......@@ -158,6 +158,10 @@ namespace interpreter {
OperandType::kIdx8) \
V(CallWide, OperandType::kReg16, OperandType::kReg16, \
OperandType::kRegCount16, OperandType::kIdx16) \
V(TailCall, OperandType::kReg8, OperandType::kReg8, OperandType::kRegCount8, \
OperandType::kIdx8) \
V(TailCallWide, OperandType::kReg16, OperandType::kReg16, \
OperandType::kRegCount16, OperandType::kIdx16) \
V(CallRuntime, OperandType::kIdx16, OperandType::kMaybeReg8, \
OperandType::kRegCount8) \
V(CallRuntimeWide, OperandType::kIdx16, OperandType::kMaybeReg16, \
......
......@@ -335,8 +335,10 @@ void InterpreterAssembler::CallEpilogue() {
}
Node* InterpreterAssembler::CallJS(Node* function, Node* context,
Node* first_arg, Node* arg_count) {
Callable callable = CodeFactory::InterpreterPushArgsAndCall(isolate());
Node* first_arg, Node* arg_count,
TailCallMode tail_call_mode) {
Callable callable =
CodeFactory::InterpreterPushArgsAndCall(isolate(), tail_call_mode);
Node* code_target = HeapConstant(callable.code());
return CallStub(callable.descriptor(), code_target, context, arg_count,
first_arg, function);
......
......@@ -84,7 +84,8 @@ class InterpreterAssembler : public compiler::CodeStubAssembler {
// arguments (not including receiver) and the first argument
// located at |first_arg|.
compiler::Node* CallJS(compiler::Node* function, compiler::Node* context,
compiler::Node* first_arg, compiler::Node* arg_count);
compiler::Node* first_arg, compiler::Node* arg_count,
TailCallMode tail_call_mode);
// Call constructor |constructor| with |arg_count| arguments (not
// including receiver) and the first argument located at
......
......@@ -922,8 +922,8 @@ void Interpreter::DoDeletePropertySloppy(InterpreterAssembler* assembler) {
DoDelete(Runtime::kDeleteProperty_Sloppy, assembler);
}
void Interpreter::DoJSCall(InterpreterAssembler* assembler) {
void Interpreter::DoJSCall(InterpreterAssembler* assembler,
TailCallMode tail_call_mode) {
Node* function_reg = __ BytecodeOperandReg(0);
Node* function = __ LoadRegister(function_reg);
Node* receiver_reg = __ BytecodeOperandReg(1);
......@@ -933,7 +933,8 @@ void Interpreter::DoJSCall(InterpreterAssembler* assembler) {
Node* args_count = __ Int32Sub(receiver_args_count, receiver_count);
Node* context = __ GetContext();
// TODO(rmcilroy): Use the call type feedback slot to call via CallStub.
Node* result = __ CallJS(function, context, receiver_arg, args_count);
Node* result =
__ CallJS(function, context, receiver_arg, args_count, tail_call_mode);
__ SetAccumulator(result);
__ Dispatch();
}
......@@ -944,7 +945,7 @@ void Interpreter::DoJSCall(InterpreterAssembler* assembler) {
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCall(InterpreterAssembler* assembler) {
DoJSCall(assembler);
DoJSCall(assembler, TailCallMode::kDisallow);
}
......@@ -953,7 +954,23 @@ void Interpreter::DoCall(InterpreterAssembler* assembler) {
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCallWide(InterpreterAssembler* assembler) {
DoJSCall(assembler);
DoJSCall(assembler, TailCallMode::kDisallow);
}
// TailCall <callable> <receiver> <arg_count>
//
// Tail call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoTailCall(InterpreterAssembler* assembler) {
DoJSCall(assembler, TailCallMode::kAllow);
}
// TailCallWide <callable> <receiver> <arg_count>
//
// Tail call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoTailCallWide(InterpreterAssembler* assembler) {
DoJSCall(assembler, TailCallMode::kAllow);
}
void Interpreter::DoCallRuntimeCommon(InterpreterAssembler* assembler) {
......@@ -1044,7 +1061,8 @@ void Interpreter::DoCallJSRuntimeCommon(InterpreterAssembler* assembler) {
Node* function = __ LoadContextSlot(native_context, context_index);
// Call the function.
Node* result = __ CallJS(function, context, first_arg, args_count);
Node* result = __ CallJS(function, context, first_arg, args_count,
TailCallMode::kDisallow);
__ SetAccumulator(result);
__ Dispatch();
}
......
......@@ -86,7 +86,7 @@ class Interpreter {
void DoKeyedStoreIC(Callable ic, InterpreterAssembler* assembler);
// Generates code to perform a JS call.
void DoJSCall(InterpreterAssembler* assembler);
void DoJSCall(InterpreterAssembler* assembler, TailCallMode tail_call_mode);
// Generates code to perform a runtime call.
void DoCallRuntimeCommon(InterpreterAssembler* assembler);
......
......@@ -1075,7 +1075,8 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- a0 : the number of arguments (not including the receiver)
// -- a2 : the address of the first argument to be pushed. Subsequent
......@@ -1100,7 +1101,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ Branch(&loop_header, gt, a2, Operand(a3));
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......@@ -2079,6 +2082,16 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
__ lb(scratch1, MemOperand(at));
__ Branch(&done, ne, scratch1, Operand(zero_reg));
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ lw(scratch3, MemOperand(fp, StandardFrameConstants::kMarkerOffset));
__ Branch(&no_interpreter_frame, ne, scratch3,
Operand(Smi::FromInt(StackFrame::STUB)));
__ lw(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
__ lw(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
......
......@@ -1067,7 +1067,8 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- a0 : the number of arguments (not including the receiver)
// -- a2 : the address of the first argument to be pushed. Subsequent
......@@ -1092,7 +1093,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ Branch(&loop_header, gt, a2, Operand(a3));
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......@@ -2074,6 +2077,16 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
__ lb(scratch1, MemOperand(at));
__ Branch(&done, ne, scratch1, Operand(zero_reg));
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ ld(scratch3, MemOperand(fp, StandardFrameConstants::kMarkerOffset));
__ Branch(&no_interpreter_frame, ne, scratch3,
Operand(Smi::FromInt(StackFrame::STUB)));
__ ld(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
__ ld(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
......
......@@ -755,7 +755,8 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rbx : the address of the first argument to be pushed. Subsequent
......@@ -771,7 +772,9 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// Call the target.
__ PushReturnAddressFrom(kScratchRegister); // Re-push return address.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
tail_call_mode),
RelocInfo::CODE_TARGET);
}
......@@ -2187,7 +2190,17 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
ExternalReference::debug_is_active_address(masm->isolate());
__ Move(kScratchRegister, debug_is_active);
__ cmpb(Operand(kScratchRegister, 0), Immediate(0));
__ j(not_equal, &done, Label::kNear);
__ j(not_equal, &done);
// Drop possible interpreter handler/stub frame.
{
Label no_interpreter_frame;
__ Cmp(Operand(rbp, StandardFrameConstants::kMarkerOffset),
Smi::FromInt(StackFrame::STUB));
__ j(not_equal, &no_interpreter_frame, Label::kNear);
__ movp(rbp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ bind(&no_interpreter_frame);
}
// Check if next frame is an arguments adaptor frame.
Label no_arguments_adaptor, formal_parameter_count_loaded;
......
......@@ -928,8 +928,7 @@ TEST(InterpreterStoreKeyedProperty) {
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterCall) {
static void TestInterpreterCall(TailCallMode tail_call_mode) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
......@@ -951,7 +950,7 @@ TEST(InterpreterCall) {
0, 1);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 1, 0)
.Call(Register(0), builder.Parameter(0), 1, 0, tail_call_mode)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -970,7 +969,7 @@ TEST(InterpreterCall) {
0, 1);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 1, 0)
.Call(Register(0), builder.Parameter(0), 1, 0, tail_call_mode)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -998,7 +997,7 @@ TEST(InterpreterCall) {
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(Register(3))
.Call(Register(0), Register(1), 3, 0)
.Call(Register(0), Register(1), 3, 0, tail_call_mode)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -1041,7 +1040,7 @@ TEST(InterpreterCall) {
.StoreAccumulatorInRegister(Register(10))
.LoadLiteral(factory->NewStringFromAsciiChecked("j"))
.StoreAccumulatorInRegister(Register(11))
.Call(Register(0), Register(1), 11, 0)
.Call(Register(0), Register(1), 11, 0, tail_call_mode)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -1062,6 +1061,9 @@ TEST(InterpreterCall) {
}
}
TEST(InterpreterCall) { TestInterpreterCall(TailCallMode::kDisallow); }
TEST(InterpreterTailCall) { TestInterpreterCall(TailCallMode::kAllow); }
static BytecodeArrayBuilder& SetRegister(BytecodeArrayBuilder& builder,
Register reg, int value,
......
......@@ -14,9 +14,9 @@
}
return f(n - 1);
}
assertThrows(()=>{ f(1e6) });
assertThrows(()=>{ f(1e5) });
%OptimizeFunctionOnNextCall(f);
assertThrows(()=>{ f(1e6) });
assertThrows(()=>{ f(1e5) });
})();
......@@ -31,9 +31,9 @@
}
return f(n - 1);
}
assertEquals("foo", f(1e6));
assertEquals("foo", f(1e5));
%OptimizeFunctionOnNextCall(f);
assertEquals("foo", f(1e6));
assertEquals("foo", f(1e5));
})();
......@@ -51,11 +51,11 @@
}
return f(n - 1);
}
assertEquals("foo", f(1e6));
assertEquals("bar", f(1e6 + 1));
assertEquals("foo", f(1e5));
assertEquals("bar", f(1e5 + 1));
%OptimizeFunctionOnNextCall(f);
assertEquals("foo", f(1e6));
assertEquals("bar", f(1e6 + 1));
assertEquals("foo", f(1e5));
assertEquals("bar", f(1e5 + 1));
})();
......@@ -74,9 +74,9 @@
function f(n) {
return f_bound(n);
}
assertEquals("foo", f(1e6));
assertEquals("foo", f(1e5));
%OptimizeFunctionOnNextCall(f);
assertEquals("foo", f(1e6));
assertEquals("foo", f(1e5));
})();
......@@ -99,9 +99,9 @@
function f(n) {
return f_bound(n);
}
assertEquals("foo", f(1e6));
assertEquals("bar", f(1e6 + 1));
assertEquals("foo", f(1e5));
assertEquals("bar", f(1e5 + 1));
%OptimizeFunctionOnNextCall(f);
assertEquals("foo", f(1e6));
assertEquals("bar", f(1e6 + 1));
assertEquals("foo", f(1e5));
assertEquals("bar", f(1e5 + 1));
})();
......@@ -44,7 +44,6 @@
'regress/regress-crbug-160010': [SKIP],
# Issue 4698: not fully supported by Turbofan yet
'es6/tail-call-simple': [SKIP],
'es6/tail-call': [PASS, NO_VARIANTS],
# Issue 3389: deopt_every_n_garbage_collections is unsafe
......@@ -850,9 +849,6 @@
'debug-liveedit-2': [FAIL],
'compiler/deopt-tonumber-compare': [FAIL],
'es6/string-search': [FAIL],
'es6/tail-call-proxies': [FAIL],
'es6/tail-call': [FAIL],
'es6/tail-call-simple': [FAIL],
'es6/mirror-collections': [FAIL],
'es6/regress/regress-468661': [FAIL],
'harmony/string-replace': [FAIL],
......
......@@ -96,6 +96,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Call operations.
builder.Call(reg, other, 1, 0)
.Call(reg, wide, 1, 0)
.TailCall(reg, other, 1, 0)
.TailCall(reg, wide, 1, 0)
.CallRuntime(Runtime::kIsArray, reg, 1)
.CallRuntime(Runtime::kIsArray, wide, 1)
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg, 1, other)
......
......@@ -643,21 +643,27 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
}
TARGET_TEST_F(InterpreterAssemblerTest, CallJS) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin = CodeFactory::InterpreterPushArgsAndCall(isolate());
Node* function = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Node* context =
m.Parameter(InterpreterDispatchDescriptor::kContextParameter);
Node* call_js = m.CallJS(function, context, first_arg, arg_count);
EXPECT_THAT(
call_js,
IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg,
function,
IsParameter(InterpreterDispatchDescriptor::kContextParameter), _,
_));
TailCallMode tail_call_modes[] = {TailCallMode::kDisallow,
TailCallMode::kAllow};
TRACED_FOREACH(TailCallMode, tail_call_mode, tail_call_modes) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin =
CodeFactory::InterpreterPushArgsAndCall(isolate(), tail_call_mode);
Node* function = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Node* context =
m.Parameter(InterpreterDispatchDescriptor::kContextParameter);
Node* call_js =
m.CallJS(function, context, first_arg, arg_count, tail_call_mode);
EXPECT_THAT(
call_js,
IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg,
function,
IsParameter(InterpreterDispatchDescriptor::kContextParameter),
_, _));
}
}
}
......
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