[cleanup] Remove always-off support for tail calls
The tail call implementation is hidden behind the --harmony-tailcalls flag, which is off-by-default (and has been unstaged since February). It is known to be broken in a variety of cases, including clusterfuzz security issues (see sample Chromium issues below). To avoid letting the implementation bitrot further on trunk, this patch removes it. Bug: v8:4698, chromium:636914, chromium:724746 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.v8:v8_linux_noi18n_rel_ng Change-Id: I9cb547101456a582374fdf7b1a3f044a9ef33e5c Reviewed-on: https://chromium-review.googlesource.com/569069 Commit-Queue: Adam Klein <adamk@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Cr-Commit-Position: refs/heads/master@{#46651}
This commit is contained in:
parent
415fd8d8d1
commit
1769f892ce
@ -9287,14 +9287,9 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) {
|
||||
debug::SetLiveEditEnabled(isolate, enable);
|
||||
}
|
||||
|
||||
bool Debug::IsTailCallEliminationEnabled(Isolate* isolate) {
|
||||
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
return internal_isolate->is_tail_call_elimination_enabled();
|
||||
}
|
||||
bool Debug::IsTailCallEliminationEnabled(Isolate* isolate) { return false; }
|
||||
|
||||
void Debug::SetTailCallEliminationEnabled(Isolate* isolate, bool enabled) {
|
||||
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
internal_isolate->SetTailCallEliminationEnabled(enabled);
|
||||
}
|
||||
|
||||
MaybeLocal<Array> Debug::GetInternalProperties(Isolate* v8_isolate,
|
||||
|
@ -1573,11 +1573,6 @@ ExternalReference ExternalReference::cpu_features() {
|
||||
return ExternalReference(&CpuFeatures::supported_);
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
Isolate* isolate) {
|
||||
return ExternalReference(isolate->is_tail_call_elimination_enabled_address());
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::promise_hook_or_debug_is_active_address(
|
||||
Isolate* isolate) {
|
||||
return ExternalReference(isolate->promise_hook_or_debug_is_active_address());
|
||||
|
@ -431,7 +431,7 @@ class RelocInfo {
|
||||
}
|
||||
static inline bool IsDebugBreakSlot(Mode mode) {
|
||||
return IsDebugBreakSlotAtPosition(mode) || IsDebugBreakSlotAtReturn(mode) ||
|
||||
IsDebugBreakSlotAtCall(mode) || IsDebugBreakSlotAtTailCall(mode);
|
||||
IsDebugBreakSlotAtCall(mode);
|
||||
}
|
||||
static inline bool IsDebugBreakSlotAtPosition(Mode mode) {
|
||||
return mode == DEBUG_BREAK_SLOT_AT_POSITION;
|
||||
@ -442,9 +442,6 @@ class RelocInfo {
|
||||
static inline bool IsDebugBreakSlotAtCall(Mode mode) {
|
||||
return mode == DEBUG_BREAK_SLOT_AT_CALL;
|
||||
}
|
||||
static inline bool IsDebugBreakSlotAtTailCall(Mode mode) {
|
||||
return mode == DEBUG_BREAK_SLOT_AT_TAIL_CALL;
|
||||
}
|
||||
static inline bool IsNone(Mode mode) {
|
||||
return mode == NONE32 || mode == NONE64;
|
||||
}
|
||||
@ -1007,9 +1004,6 @@ class ExternalReference BASE_EMBEDDED {
|
||||
|
||||
static ExternalReference cpu_features();
|
||||
|
||||
static ExternalReference is_tail_call_elimination_enabled_address(
|
||||
Isolate* isolate);
|
||||
|
||||
static ExternalReference debug_is_active_address(Isolate* isolate);
|
||||
static ExternalReference debug_hook_on_function_call_address(
|
||||
Isolate* isolate);
|
||||
|
@ -159,16 +159,6 @@ bool Expression::IsAccessorFunctionDefinition() const {
|
||||
return IsFunctionLiteral() && IsAccessorFunction(AsFunctionLiteral()->kind());
|
||||
}
|
||||
|
||||
void Expression::MarkTail() {
|
||||
if (IsConditional()) {
|
||||
AsConditional()->MarkTail();
|
||||
} else if (IsCall()) {
|
||||
AsCall()->MarkTail();
|
||||
} else if (IsBinaryOperation()) {
|
||||
AsBinaryOperation()->MarkTail();
|
||||
}
|
||||
}
|
||||
|
||||
bool Statement::IsJump() const {
|
||||
switch (node_type()) {
|
||||
#define JUMP_NODE_LIST(V) \
|
||||
|
@ -282,9 +282,6 @@ class Expression : public AstNode {
|
||||
kTest
|
||||
};
|
||||
|
||||
// Mark this expression as being in tail position.
|
||||
void MarkTail();
|
||||
|
||||
// True iff the expression is a valid reference expression.
|
||||
bool IsValidReferenceExpression() const;
|
||||
|
||||
@ -1757,12 +1754,6 @@ class Call final : public Expression {
|
||||
return IsPossiblyEvalField::decode(bit_field_);
|
||||
}
|
||||
|
||||
TailCallMode tail_call_mode() const {
|
||||
return IsTailField::decode(bit_field_) ? TailCallMode::kAllow
|
||||
: TailCallMode::kDisallow;
|
||||
}
|
||||
void MarkTail() { bit_field_ = IsTailField::update(bit_field_, true); }
|
||||
|
||||
bool only_last_arg_is_spread() {
|
||||
return !arguments_->is_empty() && arguments_->last()->IsSpread();
|
||||
}
|
||||
@ -1805,8 +1796,8 @@ class Call final : public Expression {
|
||||
|
||||
class IsUninitializedField
|
||||
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
|
||||
class IsTailField : public BitField<bool, IsUninitializedField::kNext, 1> {};
|
||||
class IsPossiblyEvalField : public BitField<bool, IsTailField::kNext, 1> {};
|
||||
class IsPossiblyEvalField
|
||||
: public BitField<bool, IsUninitializedField::kNext, 1> {};
|
||||
|
||||
FeedbackSlot ic_slot_;
|
||||
Expression* expression_;
|
||||
@ -1945,17 +1936,6 @@ class BinaryOperation final : public Expression {
|
||||
Expression* right() const { return right_; }
|
||||
void set_right(Expression* e) { right_ = e; }
|
||||
|
||||
void MarkTail() {
|
||||
switch (op()) {
|
||||
case Token::COMMA:
|
||||
case Token::AND:
|
||||
case Token::OR:
|
||||
right_->MarkTail();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode,
|
||||
FeedbackSlotCache* cache);
|
||||
|
||||
@ -2110,11 +2090,6 @@ class Conditional final : public Expression {
|
||||
void set_then_expression(Expression* e) { then_expression_ = e; }
|
||||
void set_else_expression(Expression* e) { else_expression_ = e; }
|
||||
|
||||
void MarkTail() {
|
||||
then_expression_->MarkTail();
|
||||
else_expression_->MarkTail();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
|
@ -1147,9 +1147,7 @@ void AstPrinter::VisitProperty(Property* node) {
|
||||
|
||||
void AstPrinter::VisitCall(Call* node) {
|
||||
EmbeddedVector<char, 128> buf;
|
||||
const char* name =
|
||||
node->tail_call_mode() == TailCallMode::kAllow ? "TAIL CALL" : "CALL";
|
||||
FormatSlotNode(&buf, node, name, node->CallFeedbackICSlot());
|
||||
FormatSlotNode(&buf, node, "CALL", node->CallFeedbackICSlot());
|
||||
IndentedScope indent(this, buf.start());
|
||||
|
||||
Visit(node->expression());
|
||||
|
@ -4055,7 +4055,6 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_named_captures)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_property)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_generators)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_tostring)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
|
||||
|
@ -1306,7 +1306,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1336,15 +1336,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
|
||||
// Call the target.
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ Jump(
|
||||
masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2213,102 +2212,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg <- sp (f()'s caller pc is not on the stack yet!)
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is enabled.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ mov(scratch1, Operand(is_tail_call_elimination_enabled));
|
||||
__ ldrb(scratch1, MemOperand(scratch1));
|
||||
__ cmp(scratch1, Operand(0));
|
||||
__ b(eq, &done);
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ ldr(scratch3,
|
||||
MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ cmp(scratch3, Operand(StackFrame::TypeToMarker(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.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ ldr(scratch3,
|
||||
MemOperand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ cmp(scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ b(ne, &no_arguments_adaptor);
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ mov(fp, scratch2);
|
||||
__ ldr(caller_args_count_reg,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(caller_args_count_reg);
|
||||
__ b(&formal_parameter_count_loaded);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ ldr(scratch1,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kFunctionOffset));
|
||||
__ ldr(scratch1,
|
||||
FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(caller_args_count_reg,
|
||||
FieldMemOperand(scratch1,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r1 : the function to call (checked to be a JSFunction)
|
||||
@ -2394,10 +2300,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, r0, r3, r4, r5);
|
||||
}
|
||||
|
||||
__ ldr(r2,
|
||||
FieldMemOperand(r2, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(r0);
|
||||
@ -2495,18 +2397,13 @@ void Generate_PushBoundArguments(MacroAssembler* masm) {
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r1 : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(r1);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, r0, r3, r4, r5);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
__ ldr(r3, FieldMemOperand(r1, JSBoundFunction::kBoundThisOffset));
|
||||
__ str(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2));
|
||||
@ -2524,8 +2421,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r1 : the target to call (can be any Object).
|
||||
@ -2535,10 +2431,10 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(r1, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET, eq);
|
||||
__ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE));
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET, eq);
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2549,11 +2445,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ cmp(r5, Operand(JS_PROXY_TYPE));
|
||||
__ b(ne, &non_function);
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, r0, r3, r4, r5);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ Push(r1);
|
||||
// Increase the arguments size to include the pushed function and the
|
||||
@ -2571,7 +2462,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r1);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -1329,7 +1329,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1361,15 +1361,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
|
||||
// Call the target.
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ Jump(
|
||||
masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2309,100 +2308,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg <- sp (f()'s caller pc is not on the stack yet!)
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is enabled.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ Mov(scratch1, Operand(is_tail_call_elimination_enabled));
|
||||
__ Ldrb(scratch1, MemOperand(scratch1));
|
||||
__ Cmp(scratch1, Operand(0));
|
||||
__ B(eq, &done);
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ Ldr(scratch3,
|
||||
MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Cmp(scratch3, Operand(StackFrame::TypeToMarker(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.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ Ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ldr(scratch3,
|
||||
MemOperand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Cmp(scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ B(ne, &no_arguments_adaptor);
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ mov(fp, scratch2);
|
||||
__ Ldr(caller_args_count_reg,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(caller_args_count_reg);
|
||||
__ B(&formal_parameter_count_loaded);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ Ldr(scratch1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ Ldr(scratch1,
|
||||
FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldrsw(caller_args_count_reg,
|
||||
FieldMemOperand(scratch1,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
ASM_LOCATION("Builtins::Generate_CallFunction");
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
@ -2489,10 +2397,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, x0, x3, x4, x5);
|
||||
}
|
||||
|
||||
__ Ldrsw(
|
||||
x2, FieldMemOperand(x2, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(x0);
|
||||
@ -2587,18 +2491,13 @@ void Generate_PushBoundArguments(MacroAssembler* masm) {
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
// -- x1 : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(x1);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, x0, x3, x4, x5);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
__ Ldr(x10, FieldMemOperand(x1, JSBoundFunction::kBoundThisOffset));
|
||||
__ Poke(x10, Operand(x0, LSL, kPointerSizeLog2));
|
||||
@ -2616,8 +2515,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
// -- x1 : the target to call (can be any Object).
|
||||
@ -2627,10 +2525,10 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(x1, &non_callable);
|
||||
__ Bind(&non_smi);
|
||||
__ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET, eq);
|
||||
__ Cmp(x5, JS_BOUND_FUNCTION_TYPE);
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET, eq);
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2640,11 +2538,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ Cmp(x5, JS_PROXY_TYPE);
|
||||
__ B(ne, &non_function);
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, x0, x3, x4, x5);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ Push(x1);
|
||||
// Increase the arguments size to include the pushed function and the
|
||||
@ -2662,7 +2555,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -15,74 +15,33 @@ namespace internal {
|
||||
|
||||
void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
TailCallMode::kDisallow);
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined);
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallFunction_ReceiverIsNotNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
TailCallMode::kDisallow);
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined);
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallFunction_ReceiverIsAny(MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kAny,
|
||||
TailCallMode::kDisallow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCallFunction_ReceiverIsNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
TailCallMode::kAllow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCallFunction_ReceiverIsNotNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
TailCallMode::kAllow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCallFunction_ReceiverIsAny(MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kAny, TailCallMode::kAllow);
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
|
||||
Generate_CallBoundFunctionImpl(masm, TailCallMode::kDisallow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCallBoundFunction(MacroAssembler* masm) {
|
||||
Generate_CallBoundFunctionImpl(masm, TailCallMode::kAllow);
|
||||
Generate_CallBoundFunctionImpl(masm);
|
||||
}
|
||||
|
||||
void Builtins::Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
TailCallMode::kDisallow);
|
||||
Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined);
|
||||
}
|
||||
|
||||
void Builtins::Generate_Call_ReceiverIsNotNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
TailCallMode::kDisallow);
|
||||
Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined);
|
||||
}
|
||||
|
||||
void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kAny, TailCallMode::kDisallow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCall_ReceiverIsNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
TailCallMode::kAllow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCall_ReceiverIsNotNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
TailCallMode::kAllow);
|
||||
}
|
||||
|
||||
void Builtins::Generate_TailCall_ReceiverIsAny(MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kAny, TailCallMode::kAllow);
|
||||
Generate_Call(masm, ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
|
||||
|
@ -11,66 +11,26 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
Handle<Code> Builtins::CallFunction(ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
switch (tail_call_mode) {
|
||||
case TailCallMode::kDisallow:
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return CallFunction_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return CallFunction_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return CallFunction_ReceiverIsAny();
|
||||
}
|
||||
break;
|
||||
case TailCallMode::kAllow:
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return TailCallFunction_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return TailCallFunction_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return TailCallFunction_ReceiverIsAny();
|
||||
}
|
||||
break;
|
||||
Handle<Code> Builtins::CallFunction(ConvertReceiverMode mode) {
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return CallFunction_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return CallFunction_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return CallFunction_ReceiverIsAny();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::Call(ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
switch (tail_call_mode) {
|
||||
case TailCallMode::kDisallow:
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return Call_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return Call_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return Call_ReceiverIsAny();
|
||||
}
|
||||
break;
|
||||
case TailCallMode::kAllow:
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return TailCall_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return TailCall_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return TailCall_ReceiverIsAny();
|
||||
}
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::CallBoundFunction(TailCallMode tail_call_mode) {
|
||||
switch (tail_call_mode) {
|
||||
case TailCallMode::kDisallow:
|
||||
return CallBoundFunction();
|
||||
case TailCallMode::kAllow:
|
||||
return TailCallBoundFunction();
|
||||
Handle<Code> Builtins::Call(ConvertReceiverMode mode) {
|
||||
switch (mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return Call_ReceiverIsNullOrUndefined();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
return Call_ReceiverIsNotNullOrUndefined();
|
||||
case ConvertReceiverMode::kAny:
|
||||
return Call_ReceiverIsAny();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -62,19 +62,12 @@ namespace internal {
|
||||
ASM(CallFunction_ReceiverIsNullOrUndefined) \
|
||||
ASM(CallFunction_ReceiverIsNotNullOrUndefined) \
|
||||
ASM(CallFunction_ReceiverIsAny) \
|
||||
ASM(TailCallFunction_ReceiverIsNullOrUndefined) \
|
||||
ASM(TailCallFunction_ReceiverIsNotNullOrUndefined) \
|
||||
ASM(TailCallFunction_ReceiverIsAny) \
|
||||
/* ES6 section 9.4.1.1 [[Call]] ( thisArgument, argumentsList) */ \
|
||||
ASM(CallBoundFunction) \
|
||||
ASM(TailCallBoundFunction) \
|
||||
/* ES6 section 7.3.12 Call(F, V, [argumentsList]) */ \
|
||||
ASM(Call_ReceiverIsNullOrUndefined) \
|
||||
ASM(Call_ReceiverIsNotNullOrUndefined) \
|
||||
ASM(Call_ReceiverIsAny) \
|
||||
ASM(TailCall_ReceiverIsNullOrUndefined) \
|
||||
ASM(TailCall_ReceiverIsNotNullOrUndefined) \
|
||||
ASM(TailCall_ReceiverIsAny) \
|
||||
ASM(CallVarargs) \
|
||||
TFC(CallWithSpread, CallWithSpread, 1) \
|
||||
TFC(CallWithArrayLike, CallWithArrayLike, 1) \
|
||||
@ -138,8 +131,6 @@ namespace internal {
|
||||
ASM(InterpreterPushArgsThenCallFunction) \
|
||||
ASM(InterpreterPushUndefinedAndArgsThenCallFunction) \
|
||||
ASM(InterpreterPushArgsThenCallWithFinalSpread) \
|
||||
ASM(InterpreterPushArgsThenTailCall) \
|
||||
ASM(InterpreterPushArgsThenTailCallFunction) \
|
||||
ASM(InterpreterPushArgsThenConstruct) \
|
||||
ASM(InterpreterPushArgsThenConstructFunction) \
|
||||
ASM(InterpreterPushArgsThenConstructArray) \
|
||||
|
@ -11,51 +11,36 @@ namespace internal {
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenCall(MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kAny, TailCallMode::kDisallow,
|
||||
InterpreterPushArgsMode::kOther);
|
||||
masm, ConvertReceiverMode::kAny, InterpreterPushArgsMode::kOther);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallFunction(
|
||||
MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kAny, TailCallMode::kDisallow,
|
||||
InterpreterPushArgsMode::kJSFunction);
|
||||
masm, ConvertReceiverMode::kAny, InterpreterPushArgsMode::kJSFunction);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushUndefinedAndArgsThenCall(
|
||||
MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kNullOrUndefined, TailCallMode::kDisallow,
|
||||
masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
InterpreterPushArgsMode::kOther);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushUndefinedAndArgsThenCallFunction(
|
||||
MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kNullOrUndefined, TailCallMode::kDisallow,
|
||||
masm, ConvertReceiverMode::kNullOrUndefined,
|
||||
InterpreterPushArgsMode::kJSFunction);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallWithFinalSpread(
|
||||
MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kAny, TailCallMode::kDisallow,
|
||||
masm, ConvertReceiverMode::kAny,
|
||||
InterpreterPushArgsMode::kWithFinalSpread);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenTailCall(MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kAny, TailCallMode::kAllow,
|
||||
InterpreterPushArgsMode::kOther);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenTailCallFunction(
|
||||
MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenCallImpl(
|
||||
masm, ConvertReceiverMode::kAny, TailCallMode::kAllow,
|
||||
InterpreterPushArgsMode::kJSFunction);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterPushArgsThenConstruct(MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsThenConstructImpl(
|
||||
masm, InterpreterPushArgsMode::kOther);
|
||||
|
@ -12,37 +12,25 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
Handle<Code> Builtins::InterpreterPushArgsThenCall(
|
||||
ConvertReceiverMode receiver_mode, TailCallMode tail_call_mode,
|
||||
InterpreterPushArgsMode mode) {
|
||||
ConvertReceiverMode receiver_mode, InterpreterPushArgsMode mode) {
|
||||
switch (mode) {
|
||||
case InterpreterPushArgsMode::kJSFunction:
|
||||
if (tail_call_mode == TailCallMode::kDisallow) {
|
||||
switch (receiver_mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return InterpreterPushUndefinedAndArgsThenCallFunction();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
case ConvertReceiverMode::kAny:
|
||||
return InterpreterPushArgsThenCallFunction();
|
||||
}
|
||||
} else {
|
||||
CHECK_EQ(receiver_mode, ConvertReceiverMode::kAny);
|
||||
return InterpreterPushArgsThenTailCallFunction();
|
||||
switch (receiver_mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return InterpreterPushUndefinedAndArgsThenCallFunction();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
case ConvertReceiverMode::kAny:
|
||||
return InterpreterPushArgsThenCallFunction();
|
||||
}
|
||||
case InterpreterPushArgsMode::kWithFinalSpread:
|
||||
CHECK(tail_call_mode == TailCallMode::kDisallow);
|
||||
return InterpreterPushArgsThenCallWithFinalSpread();
|
||||
case InterpreterPushArgsMode::kOther:
|
||||
if (tail_call_mode == TailCallMode::kDisallow) {
|
||||
switch (receiver_mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return InterpreterPushUndefinedAndArgsThenCall();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
case ConvertReceiverMode::kAny:
|
||||
return InterpreterPushArgsThenCall();
|
||||
}
|
||||
} else {
|
||||
CHECK_EQ(receiver_mode, ConvertReceiverMode::kAny);
|
||||
return InterpreterPushArgsThenTailCall();
|
||||
switch (receiver_mode) {
|
||||
case ConvertReceiverMode::kNullOrUndefined:
|
||||
return InterpreterPushUndefinedAndArgsThenCall();
|
||||
case ConvertReceiverMode::kNotNullOrUndefined:
|
||||
case ConvertReceiverMode::kAny:
|
||||
return InterpreterPushArgsThenCall();
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -53,17 +53,12 @@ class Builtins {
|
||||
#undef DECLARE_BUILTIN_ACCESSOR
|
||||
|
||||
// Convenience wrappers.
|
||||
Handle<Code> CallFunction(
|
||||
ConvertReceiverMode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
Handle<Code> Call(ConvertReceiverMode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
Handle<Code> CallBoundFunction(TailCallMode tail_call_mode);
|
||||
Handle<Code> CallFunction(ConvertReceiverMode = ConvertReceiverMode::kAny);
|
||||
Handle<Code> Call(ConvertReceiverMode = ConvertReceiverMode::kAny);
|
||||
Handle<Code> NonPrimitiveToPrimitive(
|
||||
ToPrimitiveHint hint = ToPrimitiveHint::kDefault);
|
||||
Handle<Code> OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint);
|
||||
Handle<Code> InterpreterPushArgsThenCall(ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode,
|
||||
InterpreterPushArgsMode mode);
|
||||
Handle<Code> InterpreterPushArgsThenConstruct(InterpreterPushArgsMode mode);
|
||||
Handle<Code> NewFunctionContext(ScopeType scope_type);
|
||||
@ -123,14 +118,11 @@ class Builtins {
|
||||
Builtins();
|
||||
|
||||
static void Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode);
|
||||
ConvertReceiverMode mode);
|
||||
|
||||
static void Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode);
|
||||
static void Generate_CallBoundFunctionImpl(MacroAssembler* masm);
|
||||
|
||||
static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode);
|
||||
static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode);
|
||||
|
||||
static void Generate_CallOrConstructVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code);
|
||||
@ -139,7 +131,7 @@ class Builtins {
|
||||
|
||||
static void Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode);
|
||||
InterpreterPushArgsMode mode);
|
||||
|
||||
static void Generate_InterpreterPushArgsThenConstructImpl(
|
||||
MacroAssembler* masm, InterpreterPushArgsMode mode);
|
||||
|
@ -978,7 +978,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1022,15 +1022,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
__ Push(edx); // Re-push return address.
|
||||
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ Jump(
|
||||
masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2381,99 +2380,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg
|
||||
// | f()'s caller pc <- sp
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is enabled.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ movzx_b(scratch1,
|
||||
Operand::StaticVariable(is_tail_call_elimination_enabled));
|
||||
__ cmp(scratch1, Immediate(0));
|
||||
__ j(equal, &done, Label::kNear);
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset),
|
||||
Immediate(StackFrame::TypeToMarker(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.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ mov(scratch2, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ cmp(Operand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset),
|
||||
Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ j(not_equal, &no_arguments_adaptor, Label::kNear);
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ mov(ebp, scratch2);
|
||||
__ mov(caller_args_count_reg,
|
||||
Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(caller_args_count_reg);
|
||||
__ jmp(&formal_parameter_count_loaded, Label::kNear);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ mov(scratch1, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ mov(scratch1,
|
||||
FieldOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(
|
||||
caller_args_count_reg,
|
||||
FieldOperand(scratch1, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3, ReturnAddressState::kOnStack, 0);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the function to call (checked to be a JSFunction)
|
||||
@ -2562,12 +2471,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- esi : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, eax, ebx, ecx, edx);
|
||||
// Reload shared function info.
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
}
|
||||
|
||||
__ mov(ebx,
|
||||
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(eax);
|
||||
@ -2670,18 +2573,13 @@ void Generate_PushBoundArguments(MacroAssembler* masm) {
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(edi);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, eax, ebx, ecx, edx);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
__ mov(ebx, FieldOperand(edi, JSBoundFunction::kBoundThisOffset));
|
||||
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ebx);
|
||||
@ -2698,8 +2596,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the target to call (can be any Object).
|
||||
@ -2709,10 +2606,10 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(edi, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
|
||||
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2723,11 +2620,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
|
||||
__ j(not_equal, &non_function);
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, eax, ebx, ecx, edx);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ PopReturnAddressTo(ecx);
|
||||
__ Push(edi);
|
||||
@ -2747,7 +2639,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -1293,7 +1293,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1323,15 +1323,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
|
||||
// Call the target.
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ Jump(
|
||||
masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2212,100 +2211,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg <- sp (f()'s caller pc is not on the stack yet!)
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is enabled.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ li(at, Operand(is_tail_call_elimination_enabled));
|
||||
__ lb(scratch1, MemOperand(at));
|
||||
__ Branch(&done, eq, scratch1, Operand(zero_reg));
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ lw(scratch3,
|
||||
MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&no_interpreter_frame, ne, scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::STUB)));
|
||||
__ lw(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ bind(&no_interpreter_frame);
|
||||
}
|
||||
|
||||
// Check if next frame is an arguments adaptor frame.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ lw(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ lw(scratch3,
|
||||
MemOperand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&no_arguments_adaptor, ne, scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ mov(fp, scratch2);
|
||||
__ lw(caller_args_count_reg,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(caller_args_count_reg);
|
||||
__ Branch(&formal_parameter_count_loaded);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ lw(scratch1,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kFunctionOffset));
|
||||
__ lw(scratch1,
|
||||
FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(caller_args_count_reg,
|
||||
FieldMemOperand(scratch1,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
@ -2394,10 +2302,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
__ lw(a2,
|
||||
FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(a0);
|
||||
@ -2415,18 +2319,13 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(a1);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
{
|
||||
__ lw(at, FieldMemOperand(a1, JSBoundFunction::kBoundThisOffset));
|
||||
@ -2508,8 +2407,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the target to call (can be any Object).
|
||||
@ -2519,9 +2417,9 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(a1, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ GetObjectType(a1, t1, t2);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE));
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2531,11 +2429,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
|
||||
__ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ Push(a1);
|
||||
// Increase the arguments size to include the pushed function and the
|
||||
@ -2554,7 +2447,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, a1);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -1294,7 +1294,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1324,15 +1324,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
|
||||
// Call the target.
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ Jump(
|
||||
masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2237,99 +2236,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg <- sp (f()'s caller pc is not on the stack yet!)
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is enabled.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ li(at, Operand(is_tail_call_elimination_enabled));
|
||||
__ Lb(scratch1, MemOperand(at));
|
||||
__ Branch(&done, eq, scratch1, Operand(zero_reg));
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ Ld(scratch3,
|
||||
MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&no_interpreter_frame, ne, scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::STUB)));
|
||||
__ Ld(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ bind(&no_interpreter_frame);
|
||||
}
|
||||
|
||||
// Check if next frame is an arguments adaptor frame.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ Ld(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ld(scratch3,
|
||||
MemOperand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&no_arguments_adaptor, ne, scratch3,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ mov(fp, scratch2);
|
||||
__ Lw(caller_args_count_reg,
|
||||
UntagSmiMemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ Branch(&formal_parameter_count_loaded);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ Ld(scratch1,
|
||||
MemOperand(fp, ArgumentsAdaptorFrameConstants::kFunctionOffset));
|
||||
__ Ld(scratch1,
|
||||
FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Lw(caller_args_count_reg,
|
||||
FieldMemOperand(scratch1,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
@ -2418,10 +2327,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
__ Lw(a2,
|
||||
FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(a0);
|
||||
@ -2439,18 +2344,13 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(a1);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
{
|
||||
__ Ld(at, FieldMemOperand(a1, JSBoundFunction::kBoundThisOffset));
|
||||
@ -2531,8 +2431,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the target to call (can be any Object).
|
||||
@ -2542,9 +2441,9 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(a1, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ GetObjectType(a1, t1, t2);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE));
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2554,11 +2453,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
|
||||
__ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, a0, t0, t1, t2);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ Push(a1);
|
||||
// Increase the arguments size to include the pushed function and the
|
||||
@ -2577,7 +2471,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, a1);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -1047,7 +1047,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
InterpreterPushArgsMode 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
|
||||
@ -1085,14 +1085,13 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl(
|
||||
__ PushReturnAddressFrom(kScratchRegister); // Re-push return address.
|
||||
|
||||
if (mode == InterpreterPushArgsMode::kJSFunction) {
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(receiver_mode,
|
||||
tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(receiver_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else if (mode == InterpreterPushArgsMode::kWithFinalSpread) {
|
||||
__ Jump(masm->isolate()->builtins()->CallWithSpread(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
__ Jump(masm->isolate()->builtins()->Call(receiver_mode, tail_call_mode),
|
||||
__ Jump(masm->isolate()->builtins()->Call(receiver_mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
@ -2467,98 +2466,9 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Drops top JavaScript frame and an arguments adaptor frame below it (if
|
||||
// present) preserving all the arguments prepared for current call.
|
||||
// Does nothing if debugger is currently active.
|
||||
// ES6 14.6.3. PrepareForTailCall
|
||||
//
|
||||
// Stack structure for the function g() tail calling f():
|
||||
//
|
||||
// ------- Caller frame: -------
|
||||
// | ...
|
||||
// | g()'s arg M
|
||||
// | ...
|
||||
// | g()'s arg 1
|
||||
// | g()'s receiver arg
|
||||
// | g()'s caller pc
|
||||
// ------- g()'s frame: -------
|
||||
// | g()'s caller fp <- fp
|
||||
// | g()'s context
|
||||
// | function pointer: g
|
||||
// | -------------------------
|
||||
// | ...
|
||||
// | ...
|
||||
// | f()'s arg N
|
||||
// | ...
|
||||
// | f()'s arg 1
|
||||
// | f()'s receiver arg
|
||||
// | f()'s caller pc <- sp
|
||||
// ----------------------
|
||||
//
|
||||
void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3) {
|
||||
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
|
||||
Comment cmnt(masm, "[ PrepareForTailCall");
|
||||
|
||||
// Prepare for tail call only if ES2015 tail call elimination is active.
|
||||
Label done;
|
||||
ExternalReference is_tail_call_elimination_enabled =
|
||||
ExternalReference::is_tail_call_elimination_enabled_address(
|
||||
masm->isolate());
|
||||
__ Move(kScratchRegister, is_tail_call_elimination_enabled);
|
||||
__ cmpb(Operand(kScratchRegister, 0), Immediate(0));
|
||||
__ j(equal, &done);
|
||||
|
||||
// Drop possible interpreter handler/stub frame.
|
||||
{
|
||||
Label no_interpreter_frame;
|
||||
__ cmpp(Operand(rbp, CommonFrameConstants::kContextOrFrameTypeOffset),
|
||||
Immediate(StackFrame::TypeToMarker(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.
|
||||
Register caller_args_count_reg = scratch1;
|
||||
Label no_arguments_adaptor, formal_parameter_count_loaded;
|
||||
__ movp(scratch2, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ cmpp(Operand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset),
|
||||
Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ j(not_equal, &no_arguments_adaptor, Label::kNear);
|
||||
|
||||
// Drop current frame and load arguments count from arguments adaptor frame.
|
||||
__ movp(rbp, scratch2);
|
||||
__ SmiToInteger32(
|
||||
caller_args_count_reg,
|
||||
Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ jmp(&formal_parameter_count_loaded, Label::kNear);
|
||||
|
||||
__ bind(&no_arguments_adaptor);
|
||||
// Load caller's formal parameter count
|
||||
__ movp(scratch1, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ movp(scratch1,
|
||||
FieldOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movsxlq(
|
||||
caller_args_count_reg,
|
||||
FieldOperand(scratch1, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
|
||||
__ bind(&formal_parameter_count_loaded);
|
||||
|
||||
ParameterCount callee_args_count(args_reg);
|
||||
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
|
||||
scratch3, ReturnAddressState::kOnStack);
|
||||
__ bind(&done);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdi : the function to call (checked to be a JSFunction)
|
||||
@ -2654,10 +2564,6 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
// -- rsi : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, rax, rbx, rcx, r8);
|
||||
}
|
||||
|
||||
__ movsxlq(
|
||||
rbx, FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
ParameterCount actual(rax);
|
||||
@ -2760,18 +2666,13 @@ void Generate_PushBoundArguments(MacroAssembler* masm) {
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdi : the function to call (checked to be a JSBoundFunction)
|
||||
// -----------------------------------
|
||||
__ AssertBoundFunction(rdi);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, rax, rbx, rcx, r8);
|
||||
}
|
||||
|
||||
// Patch the receiver to [[BoundThis]].
|
||||
StackArgumentsAccessor args(rsp, rax);
|
||||
__ movp(rbx, FieldOperand(rdi, JSBoundFunction::kBoundThisOffset));
|
||||
@ -2789,8 +2690,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdi : the target to call (can be any Object)
|
||||
@ -2801,10 +2701,10 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ JumpIfSmi(rdi, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ CmpInstanceType(rcx, JS_BOUND_FUNCTION_TYPE);
|
||||
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
|
||||
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// Check if target has a [[Call]] internal method.
|
||||
@ -2815,11 +2715,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
__ CmpInstanceType(rcx, JS_PROXY_TYPE);
|
||||
__ j(not_equal, &non_function);
|
||||
|
||||
// 0. Prepare for tail call if necessary.
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
PrepareForTailCall(masm, rax, rbx, rcx, r8);
|
||||
}
|
||||
|
||||
// 1. Runtime fallback for Proxy [[Call]].
|
||||
__ PopReturnAddressTo(kScratchRegister);
|
||||
__ Push(rdi);
|
||||
@ -2839,7 +2734,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, rdi);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
|
@ -64,17 +64,15 @@ Callable CodeFactory::LoadGlobalICInOptimizedCode(Isolate* isolate,
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::CallIC(Isolate* isolate, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
CallICStub stub(isolate, mode, tail_call_mode);
|
||||
Callable CodeFactory::CallIC(Isolate* isolate, ConvertReceiverMode mode) {
|
||||
CallICStub stub(isolate, mode);
|
||||
return make_callable(stub);
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::CallICTrampoline(Isolate* isolate,
|
||||
ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
CallICTrampolineStub stub(isolate, mode, tail_call_mode);
|
||||
ConvertReceiverMode mode) {
|
||||
CallICTrampolineStub stub(isolate, mode);
|
||||
return make_callable(stub);
|
||||
}
|
||||
|
||||
@ -305,9 +303,8 @@ Callable CodeFactory::ArgumentAdaptor(Isolate* isolate) {
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::Call(Isolate* isolate, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
return Callable(isolate->builtins()->Call(mode, tail_call_mode),
|
||||
Callable CodeFactory::Call(Isolate* isolate, ConvertReceiverMode mode) {
|
||||
return Callable(isolate->builtins()->Call(mode),
|
||||
CallTrampolineDescriptor(isolate));
|
||||
}
|
||||
|
||||
@ -324,9 +321,8 @@ Callable CodeFactory::CallWithSpread(Isolate* isolate) {
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::CallFunction(Isolate* isolate, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
return Callable(isolate->builtins()->CallFunction(mode, tail_call_mode),
|
||||
Callable CodeFactory::CallFunction(Isolate* isolate, ConvertReceiverMode mode) {
|
||||
return Callable(isolate->builtins()->CallFunction(mode),
|
||||
CallTrampolineDescriptor(isolate));
|
||||
}
|
||||
|
||||
@ -387,10 +383,10 @@ Callable CodeFactory::ConstructFunctionForwardVarargs(Isolate* isolate) {
|
||||
// static
|
||||
Callable CodeFactory::InterpreterPushArgsThenCall(
|
||||
Isolate* isolate, ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode, InterpreterPushArgsMode mode) {
|
||||
return Callable(isolate->builtins()->InterpreterPushArgsThenCall(
|
||||
receiver_mode, tail_call_mode, mode),
|
||||
InterpreterPushArgsThenCallDescriptor(isolate));
|
||||
InterpreterPushArgsMode mode) {
|
||||
return Callable(
|
||||
isolate->builtins()->InterpreterPushArgsThenCall(receiver_mode, mode),
|
||||
InterpreterPushArgsThenCallDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -29,11 +29,9 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
static Callable LoadGlobalICInOptimizedCode(Isolate* isolate,
|
||||
TypeofMode typeof_mode);
|
||||
static Callable CallIC(Isolate* isolate,
|
||||
ConvertReceiverMode mode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
ConvertReceiverMode mode = ConvertReceiverMode::kAny);
|
||||
static Callable CallICTrampoline(
|
||||
Isolate* isolate, ConvertReceiverMode mode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
Isolate* isolate, ConvertReceiverMode mode = ConvertReceiverMode::kAny);
|
||||
static Callable StoreGlobalIC(Isolate* isolate, LanguageMode mode);
|
||||
static Callable StoreGlobalICInOptimizedCode(Isolate* isolate,
|
||||
LanguageMode mode);
|
||||
@ -85,13 +83,11 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
|
||||
static Callable ArgumentAdaptor(Isolate* isolate);
|
||||
static Callable Call(Isolate* isolate,
|
||||
ConvertReceiverMode mode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
ConvertReceiverMode mode = ConvertReceiverMode::kAny);
|
||||
static Callable CallWithArrayLike(Isolate* isolate);
|
||||
static Callable CallWithSpread(Isolate* isolate);
|
||||
static Callable CallFunction(
|
||||
Isolate* isolate, ConvertReceiverMode mode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
Isolate* isolate, ConvertReceiverMode mode = ConvertReceiverMode::kAny);
|
||||
static Callable CallVarargs(Isolate* isolate);
|
||||
static Callable CallForwardVarargs(Isolate* isolate);
|
||||
static Callable CallFunctionForwardVarargs(Isolate* isolate);
|
||||
@ -104,7 +100,6 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
|
||||
static Callable InterpreterPushArgsThenCall(Isolate* isolate,
|
||||
ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode,
|
||||
InterpreterPushArgsMode mode);
|
||||
static Callable InterpreterPushArgsThenConstruct(
|
||||
Isolate* isolate, InterpreterPushArgsMode mode);
|
||||
|
@ -572,7 +572,7 @@ TF_STUB(LoadIndexedInterceptorStub, CodeStubAssembler) {
|
||||
}
|
||||
|
||||
void CallICStub::PrintState(std::ostream& os) const { // NOLINT
|
||||
os << convert_mode() << ", " << tail_call_mode();
|
||||
os << convert_mode();
|
||||
}
|
||||
|
||||
// TODO(ishell): Move to CallICAssembler.
|
||||
@ -626,8 +626,8 @@ TF_STUB(CallICStub, CodeStubAssembler) {
|
||||
BIND(&call_function);
|
||||
{
|
||||
// Call using CallFunction builtin.
|
||||
Callable callable = CodeFactory::CallFunction(
|
||||
isolate(), stub->convert_mode(), stub->tail_call_mode());
|
||||
Callable callable =
|
||||
CodeFactory::CallFunction(isolate(), stub->convert_mode());
|
||||
TailCallStub(callable, context, target, argc);
|
||||
}
|
||||
|
||||
@ -729,8 +729,7 @@ TF_STUB(CallICStub, CodeStubAssembler) {
|
||||
{
|
||||
// Call using call builtin.
|
||||
Comment("call using Call builtin");
|
||||
Callable callable_call = CodeFactory::Call(isolate(), stub->convert_mode(),
|
||||
stub->tail_call_mode());
|
||||
Callable callable_call = CodeFactory::Call(isolate(), stub->convert_mode());
|
||||
TailCallStub(callable_call, context, target, argc);
|
||||
}
|
||||
}
|
||||
@ -742,8 +741,7 @@ TF_STUB(CallICTrampolineStub, CodeStubAssembler) {
|
||||
Node* slot = Parameter(Descriptor::kSlot);
|
||||
Node* vector = LoadFeedbackVectorForStub();
|
||||
|
||||
Callable callable = CodeFactory::CallIC(isolate(), stub->convert_mode(),
|
||||
stub->tail_call_mode());
|
||||
Callable callable = CodeFactory::CallIC(isolate(), stub->convert_mode());
|
||||
TailCallStub(callable, context, target, argc, slot, vector);
|
||||
}
|
||||
|
||||
|
@ -732,23 +732,17 @@ class MathPowStub: public PlatformCodeStub {
|
||||
|
||||
class CallICStub : public TurboFanCodeStub {
|
||||
public:
|
||||
CallICStub(Isolate* isolate, ConvertReceiverMode convert_mode,
|
||||
TailCallMode tail_call_mode)
|
||||
CallICStub(Isolate* isolate, ConvertReceiverMode convert_mode)
|
||||
: TurboFanCodeStub(isolate) {
|
||||
minor_key_ = ConvertModeBits::encode(convert_mode) |
|
||||
TailCallModeBits::encode(tail_call_mode);
|
||||
minor_key_ = ConvertModeBits::encode(convert_mode);
|
||||
}
|
||||
|
||||
ConvertReceiverMode convert_mode() const {
|
||||
return ConvertModeBits::decode(minor_key_);
|
||||
}
|
||||
TailCallMode tail_call_mode() const {
|
||||
return TailCallModeBits::decode(minor_key_);
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef BitField<ConvertReceiverMode, 0, 2> ConvertModeBits;
|
||||
typedef BitField<TailCallMode, ConvertModeBits::kNext, 1> TailCallModeBits;
|
||||
|
||||
private:
|
||||
void PrintState(std::ostream& os) const final; // NOLINT
|
||||
@ -1090,9 +1084,8 @@ class StringCharCodeAtGenerator {
|
||||
|
||||
class CallICTrampolineStub : public CallICStub {
|
||||
public:
|
||||
CallICTrampolineStub(Isolate* isolate, ConvertReceiverMode convert_mode,
|
||||
TailCallMode tail_call_mode)
|
||||
: CallICStub(isolate, convert_mode, tail_call_mode) {}
|
||||
CallICTrampolineStub(Isolate* isolate, ConvertReceiverMode convert_mode)
|
||||
: CallICStub(isolate, convert_mode) {}
|
||||
|
||||
DEFINE_CALL_INTERFACE_DESCRIPTOR(CallICTrampoline);
|
||||
DEFINE_TURBOFAN_CODE_STUB(CallICTrampoline, CallICStub);
|
||||
|
@ -1490,9 +1490,8 @@ void AstGraphBuilder::VisitCall(Call* expr) {
|
||||
// Create node to perform the function call.
|
||||
CallFrequency frequency = ComputeCallFrequency(expr->CallFeedbackICSlot());
|
||||
VectorSlotPair feedback = CreateVectorSlotPair(expr->CallFeedbackICSlot());
|
||||
const Operator* call =
|
||||
javascript()->Call(args->length() + 2, frequency, feedback, receiver_hint,
|
||||
expr->tail_call_mode());
|
||||
const Operator* call = javascript()->Call(args->length() + 2, frequency,
|
||||
feedback, receiver_hint);
|
||||
Node* value = ProcessArguments(call, args->length() + 2);
|
||||
ast_context()->ProduceValue(expr, value);
|
||||
}
|
||||
|
@ -1357,8 +1357,7 @@ Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
|
||||
return ProcessCallArguments(call_op, call_args, 2 + arg_count);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode,
|
||||
ConvertReceiverMode receiver_mode,
|
||||
void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
|
||||
Node* const* args, size_t arg_count,
|
||||
int slot_id) {
|
||||
DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
|
||||
@ -1372,8 +1371,8 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode,
|
||||
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
|
||||
|
||||
CallFrequency frequency = ComputeCallFrequency(slot_id);
|
||||
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
|
||||
receiver_mode, tail_call_mode);
|
||||
const Operator* op =
|
||||
javascript()->Call(arg_count, frequency, feedback, receiver_mode);
|
||||
Node* node = nullptr;
|
||||
if (Node* simplified = TryBuildSimplifiedCall(
|
||||
op, args, static_cast<int>(arg_count), feedback.slot())) {
|
||||
@ -1385,8 +1384,7 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode,
|
||||
environment()->BindAccumulator(node, Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildCallVarArgs(TailCallMode tail_call_mode,
|
||||
ConvertReceiverMode receiver_mode) {
|
||||
void BytecodeGraphBuilder::BuildCallVarArgs(ConvertReceiverMode receiver_mode) {
|
||||
DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
|
||||
bytecode_iterator().current_bytecode()),
|
||||
receiver_mode);
|
||||
@ -1417,17 +1415,16 @@ void BytecodeGraphBuilder::BuildCallVarArgs(TailCallMode tail_call_mode,
|
||||
|
||||
Node* const* call_args =
|
||||
GetCallArgumentsFromRegister(callee, receiver_node, first_arg, arg_count);
|
||||
BuildCall(tail_call_mode, receiver_mode, call_args,
|
||||
static_cast<size_t>(2 + arg_count), slot_id);
|
||||
BuildCall(receiver_mode, call_args, static_cast<size_t>(2 + arg_count),
|
||||
slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallAnyReceiver() {
|
||||
BuildCallVarArgs(TailCallMode::kDisallow, ConvertReceiverMode::kAny);
|
||||
BuildCallVarArgs(ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallProperty() {
|
||||
BuildCallVarArgs(TailCallMode::kDisallow,
|
||||
ConvertReceiverMode::kNotNullOrUndefined);
|
||||
BuildCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallProperty0() {
|
||||
@ -1436,8 +1433,8 @@ void BytecodeGraphBuilder::VisitCallProperty0() {
|
||||
Node* receiver =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(2);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
{callee, receiver}, slot_id);
|
||||
BuildCall(ConvertReceiverMode::kNotNullOrUndefined, {callee, receiver},
|
||||
slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallProperty1() {
|
||||
@ -1448,8 +1445,8 @@ void BytecodeGraphBuilder::VisitCallProperty1() {
|
||||
Node* arg0 =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(3);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
{callee, receiver, arg0}, slot_id);
|
||||
BuildCall(ConvertReceiverMode::kNotNullOrUndefined, {callee, receiver, arg0},
|
||||
slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallProperty2() {
|
||||
@ -1462,13 +1459,12 @@ void BytecodeGraphBuilder::VisitCallProperty2() {
|
||||
Node* arg1 =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(3));
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(4);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNotNullOrUndefined,
|
||||
BuildCall(ConvertReceiverMode::kNotNullOrUndefined,
|
||||
{callee, receiver, arg0, arg1}, slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallUndefinedReceiver() {
|
||||
BuildCallVarArgs(TailCallMode::kDisallow,
|
||||
ConvertReceiverMode::kNullOrUndefined);
|
||||
BuildCallVarArgs(ConvertReceiverMode::kNullOrUndefined);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallUndefinedReceiver0() {
|
||||
@ -1476,8 +1472,7 @@ void BytecodeGraphBuilder::VisitCallUndefinedReceiver0() {
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
|
||||
Node* receiver = jsgraph()->UndefinedConstant();
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(1);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNullOrUndefined,
|
||||
{callee, receiver}, slot_id);
|
||||
BuildCall(ConvertReceiverMode::kNullOrUndefined, {callee, receiver}, slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallUndefinedReceiver1() {
|
||||
@ -1487,8 +1482,8 @@ void BytecodeGraphBuilder::VisitCallUndefinedReceiver1() {
|
||||
Node* arg0 =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(2);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNullOrUndefined,
|
||||
{callee, receiver, arg0}, slot_id);
|
||||
BuildCall(ConvertReceiverMode::kNullOrUndefined, {callee, receiver, arg0},
|
||||
slot_id);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallUndefinedReceiver2() {
|
||||
@ -1500,7 +1495,7 @@ void BytecodeGraphBuilder::VisitCallUndefinedReceiver2() {
|
||||
Node* arg1 =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(3);
|
||||
BuildCall(TailCallMode::kDisallow, ConvertReceiverMode::kNullOrUndefined,
|
||||
BuildCall(ConvertReceiverMode::kNullOrUndefined,
|
||||
{callee, receiver, arg0, arg1}, slot_id);
|
||||
}
|
||||
|
||||
@ -1517,14 +1512,6 @@ void BytecodeGraphBuilder::VisitCallWithSpread() {
|
||||
environment()->BindAccumulator(value, Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitTailCall() {
|
||||
TailCallMode tail_call_mode =
|
||||
bytecode_array_->GetIsolate()->is_tail_call_elimination_enabled()
|
||||
? TailCallMode::kAllow
|
||||
: TailCallMode::kDisallow;
|
||||
BuildCallVarArgs(tail_call_mode, ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitCallJSRuntime() {
|
||||
PrepareEagerCheckpoint();
|
||||
Node* callee =
|
||||
|
@ -159,14 +159,12 @@ class BytecodeGraphBuilder {
|
||||
void BuildLdaLookupSlot(TypeofMode typeof_mode);
|
||||
void BuildLdaLookupContextSlot(TypeofMode typeof_mode);
|
||||
void BuildLdaLookupGlobalSlot(TypeofMode typeof_mode);
|
||||
void BuildCallVarArgs(TailCallMode tail_call_mode,
|
||||
ConvertReceiverMode receiver_mode);
|
||||
void BuildCall(TailCallMode tail_call_mode, ConvertReceiverMode receiver_mode,
|
||||
Node* const* args, size_t arg_count, int slot_id);
|
||||
void BuildCall(TailCallMode tail_call_mode, ConvertReceiverMode receiver_mode,
|
||||
void BuildCallVarArgs(ConvertReceiverMode receiver_mode);
|
||||
void BuildCall(ConvertReceiverMode receiver_mode, Node* const* args,
|
||||
size_t arg_count, int slot_id);
|
||||
void BuildCall(ConvertReceiverMode receiver_mode,
|
||||
std::initializer_list<Node*> args, int slot_id) {
|
||||
BuildCall(tail_call_mode, receiver_mode, args.begin(), args.size(),
|
||||
slot_id);
|
||||
BuildCall(receiver_mode, args.begin(), args.size(), slot_id);
|
||||
}
|
||||
void BuildBinaryOp(const Operator* op);
|
||||
void BuildBinaryOpWithImmediate(const Operator* op);
|
||||
|
@ -806,9 +806,6 @@ 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:
|
||||
DCHECK(descriptor->bailout_id().IsValidForConstructStub());
|
||||
translation->BeginConstructStubFrame(
|
||||
|
@ -54,9 +54,6 @@ 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;
|
||||
|
@ -62,7 +62,6 @@ class OutputFrameStateCombine {
|
||||
enum class FrameStateType {
|
||||
kInterpretedFunction, // Represents an InterpretedFrame.
|
||||
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
|
||||
kTailCallerFunction, // Represents a frame removed by tail call elimination.
|
||||
kConstructStub, // Represents a ConstructStubFrame.
|
||||
kGetterStub, // Represents a GetterStubFrame.
|
||||
kSetterStub, // Represents a SetterStubFrame.
|
||||
|
@ -765,10 +765,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
|
||||
buffer->frame_state_descriptor =
|
||||
buffer->frame_state_descriptor->outer_state();
|
||||
while (buffer->frame_state_descriptor != nullptr &&
|
||||
(buffer->frame_state_descriptor->type() ==
|
||||
FrameStateType::kArgumentsAdaptor ||
|
||||
buffer->frame_state_descriptor->type() ==
|
||||
FrameStateType::kTailCallerFunction)) {
|
||||
buffer->frame_state_descriptor->type() ==
|
||||
FrameStateType::kArgumentsAdaptor) {
|
||||
frame_state = NodeProperties::GetFrameStateInput(frame_state);
|
||||
buffer->frame_state_descriptor =
|
||||
buffer->frame_state_descriptor->outer_state();
|
||||
|
@ -158,10 +158,6 @@ bool CanBeNullOrUndefined(Node* node) {
|
||||
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
||||
CallParameters const& p = CallParametersOf(node->op());
|
||||
// Tail calls to Function.prototype.apply are not properly supported
|
||||
// down the pipeline, so we disable this optimization completely for
|
||||
// tail calls (for now).
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
|
||||
size_t arity = p.arity();
|
||||
DCHECK_LE(2u, arity);
|
||||
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
|
||||
@ -268,8 +264,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
||||
// Change {node} to the new {JSCall} operator.
|
||||
NodeProperties::ChangeOp(
|
||||
node,
|
||||
javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
|
||||
p.tail_call_mode()));
|
||||
javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
|
||||
// Try to further reduce the JSCall {node}.
|
||||
Reduction const reduction = ReduceJSCall(node);
|
||||
return reduction.Changed() ? reduction : Changed(node);
|
||||
@ -305,8 +300,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
|
||||
}
|
||||
NodeProperties::ChangeOp(
|
||||
node,
|
||||
javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
|
||||
p.tail_call_mode()));
|
||||
javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
|
||||
// Try to further reduce the JSCall {node}.
|
||||
Reduction const reduction = ReduceJSCall(node);
|
||||
return reduction.Changed() ? reduction : Changed(node);
|
||||
@ -1131,8 +1125,7 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
|
||||
Operator const* op =
|
||||
(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
|
||||
node->opcode() == IrOpcode::kJSCallWithSpread)
|
||||
? javascript()->CallForwardVarargs(arity + 1, start_index,
|
||||
TailCallMode::kDisallow)
|
||||
? javascript()->CallForwardVarargs(arity + 1, start_index)
|
||||
: javascript()->ConstructForwardVarargs(arity + 2, start_index);
|
||||
NodeProperties::ChangeOp(node, op);
|
||||
return Changed(node);
|
||||
@ -1293,9 +1286,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
||||
arity++;
|
||||
}
|
||||
NodeProperties::ChangeOp(
|
||||
node,
|
||||
javascript()->Call(arity, p.frequency(), VectorSlotPair(),
|
||||
convert_mode, p.tail_call_mode()));
|
||||
node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
|
||||
convert_mode));
|
||||
// Try to further reduce the JSCall {node}.
|
||||
Reduction const reduction = ReduceJSCall(node);
|
||||
return reduction.Changed() ? reduction : Changed(node);
|
||||
|
@ -637,9 +637,6 @@ void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
|
||||
int const arg_count = static_cast<int>(p.arity() - 2);
|
||||
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
|
||||
Callable callable = CodeFactory::CallForwardVarargs(isolate());
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
|
||||
Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
||||
@ -657,9 +654,6 @@ void JSGenericLowering::LowerJSCall(Node* node) {
|
||||
ConvertReceiverMode const mode = p.convert_mode();
|
||||
Callable callable = CodeFactory::Call(isolate(), mode);
|
||||
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
|
||||
Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
||||
|
@ -250,36 +250,6 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
|
||||
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().ToHandle(&shared);
|
||||
|
||||
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);
|
||||
if (frame_state->opcode() == IrOpcode::kFrameState) {
|
||||
FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
|
||||
if (frame_info.type() == FrameStateType::kArgumentsAdaptor) {
|
||||
frame_state = NodeProperties::GetFrameStateInput(frame_state);
|
||||
}
|
||||
}
|
||||
|
||||
const FrameStateFunctionInfo* state_info =
|
||||
common()->CreateFrameStateFunctionInfo(
|
||||
FrameStateType::kTailCallerFunction, 0, 0, shared);
|
||||
|
||||
const Operator* op = common()->FrameState(
|
||||
BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
|
||||
const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
|
||||
Node* node0 = graph()->NewNode(op0);
|
||||
return graph()->NewNode(op, node0, node0, node0,
|
||||
jsgraph()->UndefinedConstant(), function,
|
||||
frame_state);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(bmeurer): Unify this with the witness helper functions in the
|
||||
@ -752,20 +722,6 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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::kJSCall) {
|
||||
const CallParameters& p = CallParametersOf(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
|
||||
|
@ -62,8 +62,6 @@ 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,
|
||||
Node* exception_target,
|
||||
|
@ -146,9 +146,7 @@ StringConcatParameter const& StringConcatParameterOf(Operator const* op) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CallParameters const& p) {
|
||||
os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode() << ", "
|
||||
<< p.tail_call_mode();
|
||||
return os;
|
||||
return os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode();
|
||||
}
|
||||
|
||||
const CallParameters& CallParametersOf(const Operator* op) {
|
||||
@ -158,8 +156,7 @@ const CallParameters& CallParametersOf(const Operator* op) {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
CallForwardVarargsParameters const& p) {
|
||||
return os << p.arity() << ", " << p.start_index() << ", "
|
||||
<< p.tail_call_mode();
|
||||
return os << p.arity() << ", " << p.start_index();
|
||||
}
|
||||
|
||||
CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
|
||||
@ -804,9 +801,9 @@ const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
|
||||
hints); // parameter
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::CallForwardVarargs(
|
||||
size_t arity, uint32_t start_index, TailCallMode tail_call_mode) {
|
||||
CallForwardVarargsParameters parameters(arity, start_index, tail_call_mode);
|
||||
const Operator* JSOperatorBuilder::CallForwardVarargs(size_t arity,
|
||||
uint32_t start_index) {
|
||||
CallForwardVarargsParameters parameters(arity, start_index);
|
||||
return new (zone()) Operator1<CallForwardVarargsParameters>( // --
|
||||
IrOpcode::kJSCallForwardVarargs, Operator::kNoProperties, // opcode
|
||||
"JSCallForwardVarargs", // name
|
||||
@ -816,10 +813,8 @@ const Operator* JSOperatorBuilder::CallForwardVarargs(
|
||||
|
||||
const Operator* JSOperatorBuilder::Call(size_t arity, CallFrequency frequency,
|
||||
VectorSlotPair const& feedback,
|
||||
ConvertReceiverMode convert_mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
CallParameters parameters(arity, frequency, feedback, tail_call_mode,
|
||||
convert_mode);
|
||||
ConvertReceiverMode convert_mode) {
|
||||
CallParameters parameters(arity, frequency, feedback, convert_mode);
|
||||
return new (zone()) Operator1<CallParameters>( // --
|
||||
IrOpcode::kJSCall, Operator::kNoProperties, // opcode
|
||||
"JSCall", // name
|
||||
|
@ -182,17 +182,12 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const*);
|
||||
// is used as parameter by JSCallForwardVarargs operators.
|
||||
class CallForwardVarargsParameters final {
|
||||
public:
|
||||
CallForwardVarargsParameters(size_t arity, uint32_t start_index,
|
||||
TailCallMode tail_call_mode)
|
||||
CallForwardVarargsParameters(size_t arity, uint32_t start_index)
|
||||
: bit_field_(ArityField::encode(arity) |
|
||||
StartIndexField::encode(start_index) |
|
||||
TailCallModeField::encode(tail_call_mode)) {}
|
||||
StartIndexField::encode(start_index)) {}
|
||||
|
||||
size_t arity() const { return ArityField::decode(bit_field_); }
|
||||
uint32_t start_index() const { return StartIndexField::decode(bit_field_); }
|
||||
TailCallMode tail_call_mode() const {
|
||||
return TailCallModeField::decode(bit_field_);
|
||||
}
|
||||
|
||||
bool operator==(CallForwardVarargsParameters const& that) const {
|
||||
return this->bit_field_ == that.bit_field_;
|
||||
@ -208,7 +203,6 @@ class CallForwardVarargsParameters final {
|
||||
|
||||
typedef BitField<size_t, 0, 15> ArityField;
|
||||
typedef BitField<uint32_t, 15, 15> StartIndexField;
|
||||
typedef BitField<TailCallMode, 30, 1> TailCallModeField;
|
||||
|
||||
uint32_t const bit_field_;
|
||||
};
|
||||
@ -223,11 +217,10 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
|
||||
class CallParameters final {
|
||||
public:
|
||||
CallParameters(size_t arity, CallFrequency frequency,
|
||||
VectorSlotPair const& feedback, TailCallMode tail_call_mode,
|
||||
VectorSlotPair const& feedback,
|
||||
ConvertReceiverMode convert_mode)
|
||||
: bit_field_(ArityField::encode(arity) |
|
||||
ConvertReceiverModeField::encode(convert_mode) |
|
||||
TailCallModeField::encode(tail_call_mode)),
|
||||
ConvertReceiverModeField::encode(convert_mode)),
|
||||
frequency_(frequency),
|
||||
feedback_(feedback) {}
|
||||
|
||||
@ -236,9 +229,6 @@ class CallParameters final {
|
||||
ConvertReceiverMode convert_mode() const {
|
||||
return ConvertReceiverModeField::decode(bit_field_);
|
||||
}
|
||||
TailCallMode tail_call_mode() const {
|
||||
return TailCallModeField::decode(bit_field_);
|
||||
}
|
||||
VectorSlotPair const& feedback() const { return feedback_; }
|
||||
|
||||
bool operator==(CallParameters const& that) const {
|
||||
@ -255,7 +245,6 @@ class CallParameters final {
|
||||
|
||||
typedef BitField<size_t, 0, 29> ArityField;
|
||||
typedef BitField<ConvertReceiverMode, 29, 2> ConvertReceiverModeField;
|
||||
typedef BitField<TailCallMode, 31, 1> TailCallModeField;
|
||||
|
||||
uint32_t const bit_field_;
|
||||
CallFrequency const frequency_;
|
||||
@ -726,13 +715,11 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
||||
const Operator* CreateLiteralRegExp(Handle<String> constant_pattern,
|
||||
int literal_flags, int literal_index);
|
||||
|
||||
const Operator* CallForwardVarargs(size_t arity, uint32_t start_index,
|
||||
TailCallMode tail_call_mode);
|
||||
const Operator* CallForwardVarargs(size_t arity, uint32_t start_index);
|
||||
const Operator* Call(
|
||||
size_t arity, CallFrequency frequency = CallFrequency(),
|
||||
VectorSlotPair const& feedback = VectorSlotPair(),
|
||||
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny);
|
||||
const Operator* CallWithArrayLike(CallFrequency frequency);
|
||||
const Operator* CallWithSpread(uint32_t arity);
|
||||
const Operator* CallRuntime(Runtime::FunctionId id);
|
||||
|
@ -2163,10 +2163,6 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
|
||||
if (target_type->Is(Type::Function())) {
|
||||
// Compute flags for the call.
|
||||
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
|
||||
// Patch {node} to an indirect call via CallFunctionForwardVarargs.
|
||||
Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
@ -2235,10 +2231,6 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
|
||||
|
||||
// Compute flags for the call.
|
||||
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
|
||||
Node* new_target = jsgraph()->UndefinedConstant();
|
||||
Node* argument_count = jsgraph()->Constant(arity);
|
||||
if (NeedsArgumentAdaptorFrame(shared, arity)) {
|
||||
@ -2274,10 +2266,6 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
|
||||
if (target_type->Is(Type::Function())) {
|
||||
// Compute flags for the call.
|
||||
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
|
||||
// Patch {node} to an indirect call via the CallFunction builtin.
|
||||
Callable callable = CodeFactory::CallFunction(isolate(), convert_mode);
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
@ -2293,9 +2281,8 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
|
||||
// Maybe we did at least learn something about the {receiver}.
|
||||
if (p.convert_mode() != convert_mode) {
|
||||
NodeProperties::ChangeOp(
|
||||
node,
|
||||
javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode,
|
||||
p.tail_call_mode()));
|
||||
node, javascript()->Call(p.arity(), p.frequency(), p.feedback(),
|
||||
convert_mode));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,6 @@ int CodeBreakIterator::GetModeMask() {
|
||||
int mask = 0;
|
||||
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
|
||||
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
|
||||
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL);
|
||||
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
|
||||
return mask;
|
||||
}
|
||||
@ -225,10 +224,6 @@ DebugBreakType CodeBreakIterator::GetDebugBreakType() {
|
||||
return DEBUG_BREAK_SLOT_AT_RETURN;
|
||||
} else if (RelocInfo::IsDebugBreakSlotAtCall(rmode())) {
|
||||
return DEBUG_BREAK_SLOT_AT_CALL;
|
||||
} else if (RelocInfo::IsDebugBreakSlotAtTailCall(rmode())) {
|
||||
return isolate()->is_tail_call_elimination_enabled()
|
||||
? DEBUG_BREAK_SLOT_AT_TAIL_CALL
|
||||
: DEBUG_BREAK_SLOT_AT_CALL;
|
||||
} else if (RelocInfo::IsDebugBreakSlot(rmode())) {
|
||||
return DEBUG_BREAK_SLOT;
|
||||
} else {
|
||||
@ -311,10 +306,6 @@ DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() {
|
||||
return DEBUGGER_STATEMENT;
|
||||
} else if (bytecode == interpreter::Bytecode::kReturn) {
|
||||
return DEBUG_BREAK_SLOT_AT_RETURN;
|
||||
} else if (bytecode == interpreter::Bytecode::kTailCall) {
|
||||
return isolate()->is_tail_call_elimination_enabled()
|
||||
? DEBUG_BREAK_SLOT_AT_TAIL_CALL
|
||||
: DEBUG_BREAK_SLOT_AT_CALL;
|
||||
} else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
|
||||
return DEBUG_BREAK_SLOT_AT_CALL;
|
||||
} else if (source_position_iterator_.is_statement()) {
|
||||
@ -552,8 +543,6 @@ void Debug::Break(JavaScriptFrame* frame) {
|
||||
case StepNext:
|
||||
// Step next should not break in a deeper frame than target frame.
|
||||
if (current_frame_count > target_frame_count) return;
|
||||
// For step-next, a tail call is like a return and should break.
|
||||
step_break = location.IsTailCall();
|
||||
// Fall through.
|
||||
case StepIn: {
|
||||
FrameSummary summary = FrameSummary::GetTop(frame);
|
||||
@ -1029,8 +1018,6 @@ void Debug::PrepareStep(StepAction step_action) {
|
||||
}
|
||||
UpdateHookOnFunctionCall();
|
||||
|
||||
// A step-next at a tail call is a step-out.
|
||||
if (location.IsTailCall() && step_action == StepNext) step_action = StepOut;
|
||||
// A step-next in blackboxed function is a step-out.
|
||||
if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
|
||||
|
||||
@ -1664,7 +1651,7 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
|
||||
DCHECK(!frame->is_optimized());
|
||||
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
|
||||
BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
|
||||
return location.IsReturn() || location.IsTailCall();
|
||||
return location.IsReturn();
|
||||
}
|
||||
|
||||
void Debug::ScheduleFrameRestart(StackFrame* frame) {
|
||||
|
@ -56,7 +56,6 @@ enum DebugBreakType {
|
||||
DEBUG_BREAK_SLOT,
|
||||
DEBUG_BREAK_SLOT_AT_CALL,
|
||||
DEBUG_BREAK_SLOT_AT_RETURN,
|
||||
DEBUG_BREAK_SLOT_AT_TAIL_CALL,
|
||||
};
|
||||
|
||||
enum IgnoreBreakMode {
|
||||
@ -75,9 +74,6 @@ class BreakLocation {
|
||||
|
||||
inline bool IsReturn() const { return type_ == DEBUG_BREAK_SLOT_AT_RETURN; }
|
||||
inline bool IsCall() const { return type_ == DEBUG_BREAK_SLOT_AT_CALL; }
|
||||
inline bool IsTailCall() const {
|
||||
return type_ == DEBUG_BREAK_SLOT_AT_TAIL_CALL;
|
||||
}
|
||||
inline bool IsDebugBreakSlot() const { return type_ >= DEBUG_BREAK_SLOT; }
|
||||
inline bool IsDebuggerStatement() const {
|
||||
return type_ == DEBUGGER_STATEMENT;
|
||||
|
@ -767,12 +767,6 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
case TranslatedFrame::kArgumentsAdaptor:
|
||||
DoComputeArgumentsAdaptorFrame(translated_frame, frame_index);
|
||||
break;
|
||||
case TranslatedFrame::kTailCallerFunction:
|
||||
DoComputeTailCallerFrame(translated_frame, frame_index);
|
||||
// Tail caller frame translations do not produce output frames.
|
||||
frame_index--;
|
||||
output_count_--;
|
||||
break;
|
||||
case TranslatedFrame::kConstructStub:
|
||||
DoComputeConstructStubFrame(translated_frame, frame_index);
|
||||
break;
|
||||
@ -1206,70 +1200,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(
|
||||
}
|
||||
}
|
||||
|
||||
void Deoptimizer::DoComputeTailCallerFrame(TranslatedFrame* translated_frame,
|
||||
int frame_index) {
|
||||
SharedFunctionInfo* shared = translated_frame->raw_shared_info();
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
// Tail caller frame can't be topmost.
|
||||
CHECK_NE(output_count_ - 1, frame_index);
|
||||
|
||||
if (trace_scope_ != NULL) {
|
||||
PrintF(trace_scope_->file(), " translating tail caller frame ");
|
||||
std::unique_ptr<char[]> name = shared->DebugName()->ToCString();
|
||||
PrintF(trace_scope_->file(), "%s\n", name.get());
|
||||
}
|
||||
|
||||
if (!is_bottommost) return;
|
||||
|
||||
// Drop arguments adaptor frame below current frame if it exsits.
|
||||
Address fp_address = input_->GetFramePointerAddress();
|
||||
Address adaptor_fp_address =
|
||||
Memory::Address_at(fp_address + CommonFrameConstants::kCallerFPOffset);
|
||||
|
||||
if (StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR) !=
|
||||
Memory::intptr_at(adaptor_fp_address +
|
||||
CommonFrameConstants::kContextOrFrameTypeOffset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int caller_params_count =
|
||||
Smi::cast(
|
||||
Memory::Object_at(adaptor_fp_address +
|
||||
ArgumentsAdaptorFrameConstants::kLengthOffset))
|
||||
->value();
|
||||
|
||||
int callee_params_count =
|
||||
function_->shared()->internal_formal_parameter_count();
|
||||
|
||||
// Both caller and callee parameters count do not include receiver.
|
||||
int offset = (caller_params_count - callee_params_count) * kPointerSize;
|
||||
intptr_t new_stack_fp =
|
||||
reinterpret_cast<intptr_t>(adaptor_fp_address) + offset;
|
||||
|
||||
intptr_t new_caller_frame_top = new_stack_fp +
|
||||
(callee_params_count + 1) * kPointerSize +
|
||||
CommonFrameConstants::kFixedFrameSizeAboveFp;
|
||||
|
||||
intptr_t adaptor_caller_pc = Memory::intptr_at(
|
||||
adaptor_fp_address + CommonFrameConstants::kCallerPCOffset);
|
||||
intptr_t adaptor_caller_fp = Memory::intptr_at(
|
||||
adaptor_fp_address + CommonFrameConstants::kCallerFPOffset);
|
||||
|
||||
if (trace_scope_ != NULL) {
|
||||
PrintF(trace_scope_->file(),
|
||||
" dropping caller arguments adaptor frame: offset=%d, "
|
||||
"fp: 0x%08" V8PRIxPTR " -> 0x%08" V8PRIxPTR
|
||||
", "
|
||||
"caller sp: 0x%08" V8PRIxPTR " -> 0x%08" V8PRIxPTR "\n",
|
||||
offset, stack_fp_, new_stack_fp, caller_frame_top_,
|
||||
new_caller_frame_top);
|
||||
}
|
||||
caller_frame_top_ = new_caller_frame_top;
|
||||
caller_fp_ = adaptor_caller_fp;
|
||||
caller_pc_ = adaptor_caller_pc;
|
||||
}
|
||||
|
||||
void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
|
||||
int frame_index) {
|
||||
TranslatedFrame::iterator value_iterator = translated_frame->begin();
|
||||
@ -2219,11 +2149,6 @@ void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
|
||||
buffer_->Add(height);
|
||||
}
|
||||
|
||||
void Translation::BeginTailCallerFrame(int literal_id) {
|
||||
buffer_->Add(TAIL_CALLER_FRAME);
|
||||
buffer_->Add(literal_id);
|
||||
}
|
||||
|
||||
void Translation::BeginInterpretedFrame(BailoutId bytecode_offset,
|
||||
int literal_id, unsigned height) {
|
||||
buffer_->Add(INTERPRETED_FRAME);
|
||||
@ -2354,7 +2279,6 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
|
||||
case FLOAT_STACK_SLOT:
|
||||
case DOUBLE_STACK_SLOT:
|
||||
case LITERAL:
|
||||
case TAIL_CALLER_FRAME:
|
||||
return 1;
|
||||
case BEGIN:
|
||||
case ARGUMENTS_ADAPTOR_FRAME:
|
||||
@ -2912,12 +2836,6 @@ TranslatedFrame TranslatedFrame::ArgumentsAdaptorFrame(
|
||||
shared_info, height);
|
||||
}
|
||||
|
||||
TranslatedFrame TranslatedFrame::TailCallerFrame(
|
||||
SharedFunctionInfo* shared_info) {
|
||||
return TranslatedFrame(kTailCallerFunction, shared_info->GetIsolate(),
|
||||
shared_info, 0);
|
||||
}
|
||||
|
||||
TranslatedFrame TranslatedFrame::ConstructStubFrame(
|
||||
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) {
|
||||
TranslatedFrame frame(kConstructStub, shared_info->GetIsolate(), shared_info,
|
||||
@ -2963,9 +2881,6 @@ int TranslatedFrame::GetValueCount() {
|
||||
case kJavaScriptBuiltinContinuation:
|
||||
return 1 + height_;
|
||||
|
||||
case kTailCallerFunction:
|
||||
return 1; // Function.
|
||||
|
||||
case kInvalid:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@ -3020,17 +2935,6 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
|
||||
return TranslatedFrame::ArgumentsAdaptorFrame(shared_info, height);
|
||||
}
|
||||
|
||||
case Translation::TAIL_CALLER_FRAME: {
|
||||
SharedFunctionInfo* shared_info =
|
||||
SharedFunctionInfo::cast(literal_array->get(iterator->Next()));
|
||||
if (trace_file != nullptr) {
|
||||
std::unique_ptr<char[]> name = shared_info->DebugName()->ToCString();
|
||||
PrintF(trace_file, " reading tail caller frame marker %s\n",
|
||||
name.get());
|
||||
}
|
||||
return TranslatedFrame::TailCallerFrame(shared_info);
|
||||
}
|
||||
|
||||
case Translation::CONSTRUCT_STUB_FRAME: {
|
||||
BailoutId bailout_id = BailoutId(iterator->Next());
|
||||
SharedFunctionInfo* shared_info =
|
||||
@ -3235,7 +3139,6 @@ int TranslatedState::CreateNextTranslatedValue(
|
||||
case Translation::BEGIN:
|
||||
case Translation::INTERPRETED_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::TAIL_CALLER_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
case Translation::GETTER_STUB_FRAME:
|
||||
case Translation::SETTER_STUB_FRAME:
|
||||
@ -4159,8 +4062,7 @@ void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) {
|
||||
if (new_store && value_changed) {
|
||||
materialized_store->Set(stack_frame_pointer_,
|
||||
previously_materialized_objects);
|
||||
CHECK(frames_[0].kind() == TranslatedFrame::kInterpretedFunction ||
|
||||
frames_[0].kind() == TranslatedFrame::kTailCallerFunction);
|
||||
CHECK(frames_[0].kind() == TranslatedFrame::kInterpretedFunction);
|
||||
CHECK_EQ(frame->function(), frames_[0].front().GetRawValue());
|
||||
Deoptimizer::DeoptimizeFunction(frame->function(), frame->LookupCode());
|
||||
}
|
||||
|
@ -119,7 +119,6 @@ class TranslatedFrame {
|
||||
kInterpretedFunction,
|
||||
kGetter,
|
||||
kSetter,
|
||||
kTailCallerFunction,
|
||||
kArgumentsAdaptor,
|
||||
kConstructStub,
|
||||
kBuiltinContinuation,
|
||||
@ -189,7 +188,6 @@ class TranslatedFrame {
|
||||
SharedFunctionInfo* shared_info);
|
||||
static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo* shared_info,
|
||||
int height);
|
||||
static TranslatedFrame TailCallerFrame(SharedFunctionInfo* shared_info);
|
||||
static TranslatedFrame ConstructStubFrame(BailoutId bailout_id,
|
||||
SharedFunctionInfo* shared_info,
|
||||
int height);
|
||||
@ -515,8 +513,6 @@ class Deoptimizer : public Malloced {
|
||||
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,
|
||||
@ -876,7 +872,6 @@ class TranslationIterator BASE_EMBEDDED {
|
||||
V(GETTER_STUB_FRAME) \
|
||||
V(SETTER_STUB_FRAME) \
|
||||
V(ARGUMENTS_ADAPTOR_FRAME) \
|
||||
V(TAIL_CALLER_FRAME) \
|
||||
V(DUPLICATED_OBJECT) \
|
||||
V(ARGUMENTS_ELEMENTS) \
|
||||
V(ARGUMENTS_LENGTH) \
|
||||
@ -920,7 +915,6 @@ class Translation BASE_EMBEDDED {
|
||||
void BeginInterpretedFrame(BailoutId bytecode_offset, int literal_id,
|
||||
unsigned height);
|
||||
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
|
||||
void BeginTailCallerFrame(int literal_id);
|
||||
void BeginConstructStubFrame(BailoutId bailout_id, int literal_id,
|
||||
unsigned height);
|
||||
void BeginBuiltinContinuationFrame(BailoutId bailout_id, int literal_id,
|
||||
|
@ -289,9 +289,6 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
|
||||
"Isolate::stress_deopt_count_address()");
|
||||
Add(ExternalReference::runtime_function_table_address(isolate).address(),
|
||||
"Runtime::runtime_function_table_address()");
|
||||
Add(ExternalReference::is_tail_call_elimination_enabled_address(isolate)
|
||||
.address(),
|
||||
"Isolate::is_tail_call_elimination_enabled_address()");
|
||||
Add(ExternalReference::address_of_float_abs_constant().address(),
|
||||
"float_absolute_constant");
|
||||
Add(ExternalReference::address_of_float_neg_constant().address(),
|
||||
|
@ -192,7 +192,6 @@ DEFINE_IMPLICATION(es_staging, harmony)
|
||||
#define HARMONY_INPROGRESS_BASE(V) \
|
||||
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
|
||||
V(harmony_function_sent, "harmony function.sent") \
|
||||
V(harmony_tailcalls, "harmony tail calls") \
|
||||
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_class_fields, "harmony public fields in class literals") \
|
||||
|
@ -1676,18 +1676,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ mov(r3, Operand(IntFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ mov(r0, Operand(arg_count));
|
||||
|
@ -1633,18 +1633,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ Mov(x3, IntFromSlot(expr->CallFeedbackICSlot()));
|
||||
__ Peek(x1, (arg_count + 1) * kXRegSize);
|
||||
__ Mov(x0, arg_count);
|
||||
|
@ -638,16 +638,12 @@ void FullCodeGenerator::SetExpressionAsStatementPosition(Expression* expr) {
|
||||
}
|
||||
}
|
||||
|
||||
void FullCodeGenerator::SetCallPosition(Expression* expr,
|
||||
TailCallMode tail_call_mode) {
|
||||
void FullCodeGenerator::SetCallPosition(Expression* expr) {
|
||||
if (expr->position() == kNoSourcePosition) return;
|
||||
RecordPosition(expr->position());
|
||||
if (info_->is_debug()) {
|
||||
RelocInfo::Mode mode = (tail_call_mode == TailCallMode::kAllow)
|
||||
? RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL
|
||||
: RelocInfo::DEBUG_BREAK_SLOT_AT_CALL;
|
||||
// Always emit a debug break slot before a call.
|
||||
DebugCodegen::GenerateSlot(masm_, mode);
|
||||
DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1257,9 +1253,7 @@ void FullCodeGenerator::VisitThrow(Throw* expr) {
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
Comment cmnt(masm_, (expr->tail_call_mode() == TailCallMode::kAllow)
|
||||
? "[ TailCall"
|
||||
: "[ Call");
|
||||
Comment cmnt(masm_, "[ Call");
|
||||
Expression* callee = expr->expression();
|
||||
Call::CallType call_type = expr->GetCallType();
|
||||
|
||||
|
@ -495,8 +495,7 @@ class FullCodeGenerator final : public AstVisitor<FullCodeGenerator> {
|
||||
// This is used in loop headers where we want to break for each iteration.
|
||||
void SetExpressionAsStatementPosition(Expression* expr);
|
||||
|
||||
void SetCallPosition(Expression* expr,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
void SetCallPosition(Expression* expr);
|
||||
|
||||
void SetConstructCallPosition(Expression* expr) {
|
||||
// Currently call and construct calls are treated the same wrt debugging.
|
||||
|
@ -1569,18 +1569,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ Move(edx, Immediate(IntFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
__ Move(eax, Immediate(arg_count));
|
||||
|
@ -1679,18 +1679,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
}
|
||||
|
||||
// Record source position of the IC call.
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ li(a3, Operand(IntFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ li(a0, Operand(arg_count));
|
||||
|
@ -1681,18 +1681,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
}
|
||||
|
||||
// Record source position of the IC call.
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ li(a3, Operand(IntFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ Ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ li(a0, Operand(arg_count));
|
||||
|
@ -1590,18 +1590,8 @@ void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetCallPosition(expr, expr->tail_call_mode());
|
||||
if (expr->tail_call_mode() == TailCallMode::kAllow) {
|
||||
if (FLAG_trace) {
|
||||
__ CallRuntime(Runtime::kTraceTailCall);
|
||||
}
|
||||
// Update profiling counters before the tail call since we will
|
||||
// not return to this function.
|
||||
EmitProfilingCounterHandlingForReturnSequence(true);
|
||||
}
|
||||
Handle<Code> code =
|
||||
CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode())
|
||||
.code();
|
||||
SetCallPosition(expr);
|
||||
Handle<Code> code = CodeFactory::CallICTrampoline(isolate(), mode).code();
|
||||
__ Set(rdx, IntFromSlot(expr->CallFeedbackICSlot()));
|
||||
__ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
__ Set(rax, arg_count);
|
||||
|
@ -855,21 +855,6 @@ inline std::ostream& operator<<(std::ostream& os, ConvertReceiverMode mode) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Defines whether tail call optimization is allowed.
|
||||
enum class TailCallMode : unsigned { kAllow, kDisallow };
|
||||
|
||||
inline size_t hash_value(TailCallMode mode) { return bit_cast<unsigned>(mode); }
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, TailCallMode mode) {
|
||||
switch (mode) {
|
||||
case TailCallMode::kAllow:
|
||||
return os << "ALLOW_TAIL_CALLS";
|
||||
case TailCallMode::kDisallow:
|
||||
return os << "DISALLOW_TAIL_CALLS";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Valid hints for the abstract operation OrdinaryToPrimitive,
|
||||
// implemented according to ES6, section 7.1.1.
|
||||
enum class OrdinaryToPrimitiveHint { kNumber, kString };
|
||||
|
@ -1339,13 +1339,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallAnyReceiver(Register callable,
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::TailCall(Register callable,
|
||||
RegisterList args,
|
||||
int feedback_slot) {
|
||||
OutputTailCall(callable, args, args.register_count(), feedback_slot);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(Register callable,
|
||||
RegisterList args) {
|
||||
OutputCallWithSpread(callable, args, args.register_count());
|
||||
|
@ -3066,7 +3066,6 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
||||
RegisterList args = register_allocator()->NewGrowableRegisterList();
|
||||
|
||||
bool implicit_undefined_receiver = false;
|
||||
bool is_tail_call = (expr->tail_call_mode() == TailCallMode::kAllow);
|
||||
// When a call contains a spread, a Call AST node is only created if there is
|
||||
// exactly one spread, and it is the last argument.
|
||||
bool is_spread_call = expr->only_last_arg_is_spread();
|
||||
@ -3087,7 +3086,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
case Call::GLOBAL_CALL: {
|
||||
// Receiver is undefined for global calls.
|
||||
if (!is_tail_call && !is_spread_call) {
|
||||
if (!is_spread_call) {
|
||||
implicit_undefined_receiver = true;
|
||||
} else {
|
||||
// TODO(leszeks): There's no special bytecode for tail calls or spread
|
||||
@ -3125,7 +3124,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
case Call::OTHER_CALL: {
|
||||
// Receiver is undefined for other calls.
|
||||
if (!is_tail_call && !is_spread_call) {
|
||||
if (!is_spread_call) {
|
||||
implicit_undefined_receiver = true;
|
||||
} else {
|
||||
// TODO(leszeks): There's no special bytecode for tail calls or spread
|
||||
@ -3192,12 +3191,8 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
||||
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
|
||||
|
||||
if (is_spread_call) {
|
||||
DCHECK(!is_tail_call);
|
||||
DCHECK(!implicit_undefined_receiver);
|
||||
builder()->CallWithSpread(callee, args);
|
||||
} else if (is_tail_call) {
|
||||
DCHECK(!implicit_undefined_receiver);
|
||||
builder()->TailCall(callee, args, feedback_slot_index);
|
||||
} else if (call_type == Call::NAMED_PROPERTY_CALL ||
|
||||
call_type == Call::KEYED_PROPERTY_CALL) {
|
||||
DCHECK(!implicit_undefined_receiver);
|
||||
|
@ -179,8 +179,6 @@ namespace interpreter {
|
||||
OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
|
||||
V(CallWithSpread, AccumulatorUse::kWrite, OperandType::kReg, \
|
||||
OperandType::kRegList, OperandType::kRegCount) \
|
||||
V(TailCall, AccumulatorUse::kWrite, OperandType::kReg, \
|
||||
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
|
||||
V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \
|
||||
OperandType::kRegList, OperandType::kRegCount) \
|
||||
V(CallRuntimeForPair, AccumulatorUse::kNone, OperandType::kRuntimeId, \
|
||||
@ -654,7 +652,6 @@ class V8_EXPORT_PRIVATE Bytecodes final {
|
||||
bytecode == Bytecode::kCallUndefinedReceiver0 ||
|
||||
bytecode == Bytecode::kCallUndefinedReceiver1 ||
|
||||
bytecode == Bytecode::kCallUndefinedReceiver2 ||
|
||||
bytecode == Bytecode::kTailCall ||
|
||||
bytecode == Bytecode::kConstruct ||
|
||||
bytecode == Bytecode::kCallWithSpread ||
|
||||
bytecode == Bytecode::kConstructWithSpread ||
|
||||
@ -783,7 +780,6 @@ class V8_EXPORT_PRIVATE Bytecodes final {
|
||||
case Bytecode::kCallUndefinedReceiver2:
|
||||
return ConvertReceiverMode::kNullOrUndefined;
|
||||
case Bytecode::kCallAnyReceiver:
|
||||
case Bytecode::kTailCall:
|
||||
case Bytecode::kConstruct:
|
||||
case Bytecode::kCallWithSpread:
|
||||
case Bytecode::kConstructWithSpread:
|
||||
|
@ -563,7 +563,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(
|
||||
compiler::Node* function, compiler::Node* context,
|
||||
compiler::Node* first_arg, compiler::Node* arg_count,
|
||||
compiler::Node* slot_id, compiler::Node* feedback_vector,
|
||||
ConvertReceiverMode receiver_mode, TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode receiver_mode) {
|
||||
// Static checks to assert it is safe to examine the type feedback element.
|
||||
// We don't know that we have a weak cell. We might have a private symbol
|
||||
// or an AllocationSite, but the memory is safe to examine.
|
||||
@ -605,8 +605,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(
|
||||
{
|
||||
// Call using call function builtin.
|
||||
Callable callable = CodeFactory::InterpreterPushArgsThenCall(
|
||||
isolate(), receiver_mode, tail_call_mode,
|
||||
InterpreterPushArgsMode::kJSFunction);
|
||||
isolate(), receiver_mode, InterpreterPushArgsMode::kJSFunction);
|
||||
Node* code_target = HeapConstant(callable.code());
|
||||
Node* ret_value = CallStub(callable.descriptor(), code_target, context,
|
||||
arg_count, first_arg, function);
|
||||
@ -723,8 +722,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(
|
||||
Comment("invoke using Call builtin");
|
||||
// Call using call builtin.
|
||||
Callable callable_call = CodeFactory::InterpreterPushArgsThenCall(
|
||||
isolate(), receiver_mode, tail_call_mode,
|
||||
InterpreterPushArgsMode::kOther);
|
||||
isolate(), receiver_mode, InterpreterPushArgsMode::kOther);
|
||||
Node* code_target_call = HeapConstant(callable_call.code());
|
||||
Node* ret_value = CallStub(callable_call.descriptor(), code_target_call,
|
||||
context, arg_count, first_arg, function);
|
||||
@ -738,15 +736,13 @@ Node* InterpreterAssembler::CallJSWithFeedback(
|
||||
|
||||
Node* InterpreterAssembler::CallJS(Node* function, Node* context,
|
||||
Node* first_arg, Node* arg_count,
|
||||
ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode) {
|
||||
ConvertReceiverMode receiver_mode) {
|
||||
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
|
||||
DCHECK(Bytecodes::IsCallOrConstruct(bytecode_) ||
|
||||
bytecode_ == Bytecode::kInvokeIntrinsic);
|
||||
DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), receiver_mode);
|
||||
Callable callable = CodeFactory::InterpreterPushArgsThenCall(
|
||||
isolate(), receiver_mode, tail_call_mode,
|
||||
InterpreterPushArgsMode::kOther);
|
||||
isolate(), receiver_mode, InterpreterPushArgsMode::kOther);
|
||||
Node* code_target = HeapConstant(callable.code());
|
||||
|
||||
return CallStub(callable.descriptor(), code_target, context, arg_count,
|
||||
@ -758,7 +754,7 @@ Node* InterpreterAssembler::CallJSWithSpread(Node* function, Node* context,
|
||||
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
|
||||
DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), ConvertReceiverMode::kAny);
|
||||
Callable callable = CodeFactory::InterpreterPushArgsThenCall(
|
||||
isolate(), ConvertReceiverMode::kAny, TailCallMode::kDisallow,
|
||||
isolate(), ConvertReceiverMode::kAny,
|
||||
InterpreterPushArgsMode::kWithFinalSpread);
|
||||
Node* code_target = HeapConstant(callable.code());
|
||||
|
||||
|
@ -124,19 +124,20 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
|
||||
// If the |receiver_mode| is kNullOrUndefined, then the receiver is implicitly
|
||||
// undefined and |first_arg| is the first parameter. Otherwise, |first_arg| is
|
||||
// the receiver and it is converted according to |receiver_mode|.
|
||||
compiler::Node* CallJSWithFeedback(
|
||||
compiler::Node* function, compiler::Node* context,
|
||||
compiler::Node* first_arg, compiler::Node* arg_count,
|
||||
compiler::Node* slot_id, compiler::Node* feedback_vector,
|
||||
ConvertReceiverMode receiver_mode, TailCallMode tail_call_mode);
|
||||
compiler::Node* CallJSWithFeedback(compiler::Node* function,
|
||||
compiler::Node* context,
|
||||
compiler::Node* first_arg,
|
||||
compiler::Node* arg_count,
|
||||
compiler::Node* slot_id,
|
||||
compiler::Node* feedback_vector,
|
||||
ConvertReceiverMode receiver_mode);
|
||||
|
||||
// Call JSFunction or Callable |function| with |arg_count| arguments (not
|
||||
// including receiver) and the first argument located at |first_arg|, possibly
|
||||
// including the receiver depending on |receiver_mode|.
|
||||
compiler::Node* CallJS(compiler::Node* function, compiler::Node* context,
|
||||
compiler::Node* first_arg, compiler::Node* arg_count,
|
||||
ConvertReceiverMode receiver_mode,
|
||||
TailCallMode tail_call_mode);
|
||||
ConvertReceiverMode receiver_mode);
|
||||
|
||||
// Call JSFunction or Callable |function| with |arg_count|
|
||||
// arguments (not including receiver) and the first argument
|
||||
|
@ -1749,7 +1749,7 @@ class InterpreterJSCallAssembler : public InterpreterAssembler {
|
||||
: InterpreterAssembler(state, bytecode, operand_scale) {}
|
||||
|
||||
// Generates code to perform a JS call that collects type feedback.
|
||||
void JSCall(ConvertReceiverMode receiver_mode, TailCallMode tail_call_mode) {
|
||||
void JSCall(ConvertReceiverMode receiver_mode) {
|
||||
Node* function_reg = BytecodeOperandReg(0);
|
||||
Node* function = LoadRegister(function_reg);
|
||||
Node* first_arg_reg = BytecodeOperandReg(1);
|
||||
@ -1767,9 +1767,8 @@ class InterpreterJSCallAssembler : public InterpreterAssembler {
|
||||
Node* slot_id = BytecodeOperandIdx(3);
|
||||
Node* feedback_vector = LoadFeedbackVector();
|
||||
Node* context = GetContext();
|
||||
Node* result =
|
||||
CallJSWithFeedback(function, context, first_arg, args_count, slot_id,
|
||||
feedback_vector, receiver_mode, tail_call_mode);
|
||||
Node* result = CallJSWithFeedback(function, context, first_arg, args_count,
|
||||
slot_id, feedback_vector, receiver_mode);
|
||||
SetAccumulator(result);
|
||||
Dispatch();
|
||||
}
|
||||
@ -1832,11 +1831,11 @@ class InterpreterJSCallAssembler : public InterpreterAssembler {
|
||||
// |arg_count| arguments in subsequent registers. Collect type feedback
|
||||
// into |feedback_slot_id|
|
||||
IGNITION_HANDLER(CallAnyReceiver, InterpreterJSCallAssembler) {
|
||||
JSCall(ConvertReceiverMode::kAny, TailCallMode::kDisallow);
|
||||
JSCall(ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
IGNITION_HANDLER(CallProperty, InterpreterJSCallAssembler) {
|
||||
JSCall(ConvertReceiverMode::kNotNullOrUndefined, TailCallMode::kDisallow);
|
||||
JSCall(ConvertReceiverMode::kNotNullOrUndefined);
|
||||
}
|
||||
|
||||
IGNITION_HANDLER(CallProperty0, InterpreterJSCallAssembler) {
|
||||
@ -1852,7 +1851,7 @@ IGNITION_HANDLER(CallProperty2, InterpreterJSCallAssembler) {
|
||||
}
|
||||
|
||||
IGNITION_HANDLER(CallUndefinedReceiver, InterpreterJSCallAssembler) {
|
||||
JSCall(ConvertReceiverMode::kNullOrUndefined, TailCallMode::kDisallow);
|
||||
JSCall(ConvertReceiverMode::kNullOrUndefined);
|
||||
}
|
||||
|
||||
IGNITION_HANDLER(CallUndefinedReceiver0, InterpreterJSCallAssembler) {
|
||||
@ -1867,15 +1866,6 @@ IGNITION_HANDLER(CallUndefinedReceiver2, InterpreterJSCallAssembler) {
|
||||
JSCallN(2, ConvertReceiverMode::kNullOrUndefined);
|
||||
}
|
||||
|
||||
// TailCall <callable> <receiver> <arg_count> <feedback_slot_id>
|
||||
//
|
||||
// Tail call a JSfunction or Callable in |callable| with the |receiver| and
|
||||
// |arg_count| arguments in subsequent registers. Collect type feedback
|
||||
// into |feedback_slot_id|
|
||||
IGNITION_HANDLER(TailCall, InterpreterJSCallAssembler) {
|
||||
JSCall(ConvertReceiverMode::kAny, TailCallMode::kAllow);
|
||||
}
|
||||
|
||||
// CallRuntime <function_id> <first_arg> <arg_count>
|
||||
//
|
||||
// Call the runtime function |function_id| with the first argument in
|
||||
@ -1952,7 +1942,7 @@ IGNITION_HANDLER(CallJSRuntime, InterpreterAssembler) {
|
||||
|
||||
// Call the function.
|
||||
Node* result = CallJS(function, context, first_arg, args_count,
|
||||
ConvertReceiverMode::kAny, TailCallMode::kDisallow);
|
||||
ConvertReceiverMode::kAny);
|
||||
SetAccumulator(result);
|
||||
Dispatch();
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ Node* IntrinsicsGenerator::Call(Node* args_reg, Node* arg_count,
|
||||
}
|
||||
|
||||
Node* result = __ CallJS(function, context, receiver_arg, target_args_count,
|
||||
ConvertReceiverMode::kAny, TailCallMode::kDisallow);
|
||||
ConvertReceiverMode::kAny);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -3646,15 +3646,6 @@ std::string Isolate::GetTurboCfgFileName() {
|
||||
}
|
||||
}
|
||||
|
||||
void Isolate::SetTailCallEliminationEnabled(bool enabled) {
|
||||
if (is_tail_call_elimination_enabled_ == enabled) return;
|
||||
is_tail_call_elimination_enabled_ = enabled;
|
||||
// TODO(ishell): Introduce DependencyGroup::kTailCallChangedGroup to
|
||||
// deoptimize only those functions that are affected by the change of this
|
||||
// flag.
|
||||
internal::Deoptimizer::DeoptimizeAll(this);
|
||||
}
|
||||
|
||||
// Heap::detached_contexts tracks detached contexts as pairs
|
||||
// (number of GC since the context was detached, the context).
|
||||
void Isolate::AddDetachedContext(Handle<Context> context) {
|
||||
|
@ -1174,15 +1174,6 @@ class Isolate {
|
||||
void RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
|
||||
Handle<Object> parent);
|
||||
|
||||
// Support for dynamically disabling tail call elimination.
|
||||
Address is_tail_call_elimination_enabled_address() {
|
||||
return reinterpret_cast<Address>(&is_tail_call_elimination_enabled_);
|
||||
}
|
||||
bool is_tail_call_elimination_enabled() const {
|
||||
return is_tail_call_elimination_enabled_;
|
||||
}
|
||||
void SetTailCallEliminationEnabled(bool enabled);
|
||||
|
||||
void AddDetachedContext(Handle<Context> context);
|
||||
void CheckDetachedContextsAfterGC();
|
||||
|
||||
|
@ -14582,15 +14582,6 @@ 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();
|
||||
|
@ -160,8 +160,6 @@ void ParseInfo::InitFromIsolate(Isolate* isolate) {
|
||||
set_hash_seed(isolate->heap()->HashSeed());
|
||||
set_stack_limit(isolate->stack_guard()->real_climit());
|
||||
set_unicode_cache(isolate->unicode_cache());
|
||||
set_tail_call_elimination_enabled(
|
||||
isolate->is_tail_call_elimination_enabled());
|
||||
set_runtime_call_stats(isolate->counters()->runtime_call_stats());
|
||||
set_ast_string_constants(isolate->ast_string_constants());
|
||||
if (FLAG_block_coverage && isolate->is_block_code_coverage()) {
|
||||
|
@ -80,8 +80,6 @@ class V8_EXPORT_PRIVATE ParseInfo : public CompileJobFinishCallback {
|
||||
set_is_named_expression)
|
||||
FLAG_ACCESSOR(kDebug, is_debug, set_is_debug)
|
||||
FLAG_ACCESSOR(kSerializing, will_serialize, set_will_serialize)
|
||||
FLAG_ACCESSOR(kTailCallEliminationEnabled, is_tail_call_elimination_enabled,
|
||||
set_tail_call_elimination_enabled)
|
||||
|
||||
#undef FLAG_ACCESSOR
|
||||
|
||||
@ -280,8 +278,7 @@ class V8_EXPORT_PRIVATE ParseInfo : public CompileJobFinishCallback {
|
||||
kIsNamedExpression = 1 << 8,
|
||||
kDebug = 1 << 9,
|
||||
kSerializing = 1 << 10,
|
||||
kTailCallEliminationEnabled = 1 << 11,
|
||||
kAstValueFactoryOwned = 1 << 12,
|
||||
kAstValueFactoryOwned = 1 << 11,
|
||||
};
|
||||
|
||||
//------------- Inputs to parsing and scope analysis -----------------------
|
||||
|
@ -270,7 +270,6 @@ class ParserBase {
|
||||
default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile),
|
||||
function_literal_id_(0),
|
||||
allow_natives_(false),
|
||||
allow_tailcalls_(false),
|
||||
allow_harmony_do_expressions_(false),
|
||||
allow_harmony_function_sent_(false),
|
||||
allow_harmony_restrictive_generators_(false),
|
||||
@ -285,7 +284,6 @@ class ParserBase {
|
||||
void set_allow_##name(bool allow) { allow_##name##_ = allow; }
|
||||
|
||||
ALLOW_ACCESSORS(natives);
|
||||
ALLOW_ACCESSORS(tailcalls);
|
||||
ALLOW_ACCESSORS(harmony_do_expressions);
|
||||
ALLOW_ACCESSORS(harmony_function_sent);
|
||||
ALLOW_ACCESSORS(harmony_restrictive_generators);
|
||||
@ -376,56 +374,6 @@ class ParserBase {
|
||||
Scope* scope;
|
||||
};
|
||||
|
||||
class TailCallExpressionList {
|
||||
public:
|
||||
explicit TailCallExpressionList(Zone* zone)
|
||||
: zone_(zone), expressions_(0, zone), has_explicit_tail_calls_(false) {}
|
||||
|
||||
const ZoneList<ExpressionT>& expressions() const { return expressions_; }
|
||||
const Scanner::Location& location() const { return loc_; }
|
||||
|
||||
bool has_explicit_tail_calls() const { return has_explicit_tail_calls_; }
|
||||
|
||||
void Swap(TailCallExpressionList& other) {
|
||||
expressions_.Swap(&other.expressions_);
|
||||
std::swap(loc_, other.loc_);
|
||||
std::swap(has_explicit_tail_calls_, other.has_explicit_tail_calls_);
|
||||
}
|
||||
|
||||
void AddImplicitTailCall(ExpressionT expr) {
|
||||
expressions_.Add(expr, zone_);
|
||||
}
|
||||
|
||||
void Append(const TailCallExpressionList& other) {
|
||||
if (!has_explicit_tail_calls()) {
|
||||
loc_ = other.loc_;
|
||||
has_explicit_tail_calls_ = other.has_explicit_tail_calls_;
|
||||
}
|
||||
expressions_.AddAll(other.expressions_, zone_);
|
||||
}
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
ZoneList<ExpressionT> expressions_;
|
||||
Scanner::Location loc_;
|
||||
bool has_explicit_tail_calls_;
|
||||
};
|
||||
|
||||
// Defines whether tail call expressions are allowed or not.
|
||||
enum class ReturnExprContext {
|
||||
// We are inside return statement which is allowed to contain tail call
|
||||
// expressions. Tail call expressions are allowed.
|
||||
kInsideValidReturnStatement,
|
||||
|
||||
// We are inside a block in which tail call expressions are allowed but
|
||||
// not yet inside a return statement.
|
||||
kInsideValidBlock,
|
||||
|
||||
// Tail call expressions are not allowed in the following blocks.
|
||||
kInsideTryBlock,
|
||||
kInsideForInOfBody,
|
||||
};
|
||||
|
||||
class FunctionState final : public BlockState {
|
||||
public:
|
||||
FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
|
||||
@ -456,27 +404,10 @@ class ParserBase {
|
||||
return destructuring_assignments_to_rewrite_;
|
||||
}
|
||||
|
||||
TailCallExpressionList& tail_call_expressions() {
|
||||
return tail_call_expressions_;
|
||||
}
|
||||
void AddImplicitTailCallExpression(ExpressionT expression) {
|
||||
if (return_expr_context() ==
|
||||
ReturnExprContext::kInsideValidReturnStatement) {
|
||||
tail_call_expressions_.AddImplicitTailCall(expression);
|
||||
}
|
||||
}
|
||||
|
||||
ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() {
|
||||
return &reported_errors_;
|
||||
}
|
||||
|
||||
ReturnExprContext return_expr_context() const {
|
||||
return return_expr_context_;
|
||||
}
|
||||
void set_return_expr_context(ReturnExprContext context) {
|
||||
return_expr_context_ = context;
|
||||
}
|
||||
|
||||
ZoneList<ExpressionT>* non_patterns_to_rewrite() {
|
||||
return &non_patterns_to_rewrite_;
|
||||
}
|
||||
@ -537,8 +468,6 @@ class ParserBase {
|
||||
DeclarationScope* scope_;
|
||||
|
||||
ZoneList<DestructuringAssignment> destructuring_assignments_to_rewrite_;
|
||||
TailCallExpressionList tail_call_expressions_;
|
||||
ReturnExprContext return_expr_context_;
|
||||
ZoneList<ExpressionT> non_patterns_to_rewrite_;
|
||||
|
||||
ZoneList<typename ExpressionClassifier::Error> reported_errors_;
|
||||
@ -557,48 +486,6 @@ class ParserBase {
|
||||
friend Impl;
|
||||
};
|
||||
|
||||
// This scope sets current ReturnExprContext to given value.
|
||||
class ReturnExprScope {
|
||||
public:
|
||||
explicit ReturnExprScope(FunctionState* function_state,
|
||||
ReturnExprContext return_expr_context)
|
||||
: function_state_(function_state),
|
||||
sav_return_expr_context_(function_state->return_expr_context()) {
|
||||
// Don't update context if we are requested to enable tail call
|
||||
// expressions but current block does not allow them.
|
||||
if (return_expr_context !=
|
||||
ReturnExprContext::kInsideValidReturnStatement ||
|
||||
sav_return_expr_context_ == ReturnExprContext::kInsideValidBlock) {
|
||||
function_state->set_return_expr_context(return_expr_context);
|
||||
}
|
||||
}
|
||||
~ReturnExprScope() {
|
||||
function_state_->set_return_expr_context(sav_return_expr_context_);
|
||||
}
|
||||
|
||||
private:
|
||||
FunctionState* function_state_;
|
||||
ReturnExprContext sav_return_expr_context_;
|
||||
};
|
||||
|
||||
// Collects all return expressions at tail call position in this scope
|
||||
// to a separate list.
|
||||
class CollectExpressionsInTailPositionToListScope {
|
||||
public:
|
||||
CollectExpressionsInTailPositionToListScope(FunctionState* function_state,
|
||||
TailCallExpressionList* list)
|
||||
: function_state_(function_state), list_(list) {
|
||||
function_state->tail_call_expressions().Swap(*list_);
|
||||
}
|
||||
~CollectExpressionsInTailPositionToListScope() {
|
||||
function_state_->tail_call_expressions().Swap(*list_);
|
||||
}
|
||||
|
||||
private:
|
||||
FunctionState* function_state_;
|
||||
TailCallExpressionList* list_;
|
||||
};
|
||||
|
||||
struct DeclarationDescriptor {
|
||||
enum Kind { NORMAL, PARAMETER };
|
||||
Scope* scope;
|
||||
@ -641,15 +528,13 @@ class ParserBase {
|
||||
scope(nullptr),
|
||||
init_block(parser->impl()->NullBlock()),
|
||||
inner_block(parser->impl()->NullBlock()),
|
||||
bound_names(1, parser->zone()),
|
||||
tail_call_expressions(parser->zone()) {}
|
||||
bound_names(1, parser->zone()) {}
|
||||
IdentifierT name;
|
||||
ExpressionT pattern;
|
||||
Scope* scope;
|
||||
BlockT init_block;
|
||||
BlockT inner_block;
|
||||
ZoneList<const AstRawString*> bound_names;
|
||||
TailCallExpressionList tail_call_expressions;
|
||||
};
|
||||
|
||||
struct ForInfo {
|
||||
@ -1574,7 +1459,6 @@ class ParserBase {
|
||||
int function_literal_id_;
|
||||
|
||||
bool allow_natives_;
|
||||
bool allow_tailcalls_;
|
||||
bool allow_harmony_do_expressions_;
|
||||
bool allow_harmony_function_sent_;
|
||||
bool allow_harmony_restrictive_generators_;
|
||||
@ -1597,8 +1481,6 @@ ParserBase<Impl>::FunctionState::FunctionState(
|
||||
outer_function_state_(*function_state_stack),
|
||||
scope_(scope),
|
||||
destructuring_assignments_to_rewrite_(16, scope->zone()),
|
||||
tail_call_expressions_(scope->zone()),
|
||||
return_expr_context_(ReturnExprContext::kInsideValidBlock),
|
||||
non_patterns_to_rewrite_(0, scope->zone()),
|
||||
reported_errors_(16, scope->zone()),
|
||||
next_function_is_likely_called_(false),
|
||||
@ -4198,7 +4080,6 @@ void ParserBase<Impl>::ParseFunctionBody(
|
||||
impl()->CreateFunctionNameAssignment(function_name, pos, function_type,
|
||||
function_scope, result,
|
||||
kFunctionNameAssignmentIndex);
|
||||
impl()->MarkCollectedTailCallExpressions();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
@ -4353,10 +4234,6 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
|
||||
// Single-expression body
|
||||
has_braces = false;
|
||||
int pos = position();
|
||||
DCHECK(ReturnExprContext::kInsideValidBlock ==
|
||||
function_state_->return_expr_context());
|
||||
ReturnExprScope allow_tail_calls(
|
||||
function_state_, ReturnExprContext::kInsideValidReturnStatement);
|
||||
body = impl()->NewStatementList(1);
|
||||
impl()->AddParameterInitializationBlock(
|
||||
formal_parameters, body, kind == kAsyncArrowFunction, CHECK_OK);
|
||||
@ -4371,13 +4248,8 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
|
||||
impl()->RewriteNonPattern(CHECK_OK);
|
||||
body->Add(BuildReturnStatement(expression, expression->position()),
|
||||
zone());
|
||||
if (allow_tailcalls() && !is_sloppy(language_mode())) {
|
||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||
impl()->MarkTailPosition(expression);
|
||||
}
|
||||
}
|
||||
expected_property_count = function_state.expected_property_count();
|
||||
impl()->MarkCollectedTailCallExpressions();
|
||||
}
|
||||
|
||||
formal_parameters.scope->set_end_position(scanner()->location().end_pos);
|
||||
@ -5317,21 +5189,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement(
|
||||
return_value = impl()->GetLiteralUndefined(position());
|
||||
}
|
||||
} else {
|
||||
if (IsDerivedConstructor(function_state_->kind())) {
|
||||
// Because of the return code rewriting that happens in case of a subclass
|
||||
// constructor we don't want to accept tail calls, therefore we don't set
|
||||
// ReturnExprScope to kInsideValidReturnStatement here.
|
||||
return_value = ParseExpression(true, CHECK_OK);
|
||||
} else {
|
||||
ReturnExprScope maybe_allow_tail_calls(
|
||||
function_state_, ReturnExprContext::kInsideValidReturnStatement);
|
||||
return_value = ParseExpression(true, CHECK_OK);
|
||||
|
||||
if (allow_tailcalls() && !is_sloppy(language_mode()) && !is_resumable()) {
|
||||
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
||||
function_state_->AddImplicitTailCallExpression(return_value);
|
||||
}
|
||||
}
|
||||
return_value = ParseExpression(true, CHECK_OK);
|
||||
}
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
return_value = impl()->RewriteReturn(return_value, loc.beg_pos);
|
||||
@ -5536,12 +5394,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
|
||||
Expect(Token::TRY, CHECK_OK);
|
||||
int pos = position();
|
||||
|
||||
BlockT try_block = impl()->NullBlock();
|
||||
{
|
||||
ReturnExprScope no_tail_calls(function_state_,
|
||||
ReturnExprContext::kInsideTryBlock);
|
||||
try_block = ParseBlock(nullptr, CHECK_OK);
|
||||
}
|
||||
BlockT try_block = ParseBlock(nullptr, CHECK_OK);
|
||||
|
||||
CatchInfo catch_info(this);
|
||||
|
||||
@ -5560,9 +5413,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
|
||||
catch_info.scope->set_start_position(scanner()->location().beg_pos);
|
||||
|
||||
{
|
||||
CollectExpressionsInTailPositionToListScope
|
||||
collect_tail_call_expressions_scope(
|
||||
function_state_, &catch_info.tail_call_expressions);
|
||||
BlockState catch_block_state(&scope_, catch_info.scope);
|
||||
|
||||
catch_block = factory()->NewBlock(nullptr, 16, false, kNoSourcePosition);
|
||||
@ -5728,8 +5578,6 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations(
|
||||
|
||||
StatementT final_loop = impl()->NullStatement();
|
||||
{
|
||||
ReturnExprScope no_tail_calls(function_state_,
|
||||
ReturnExprContext::kInsideForInOfBody);
|
||||
BlockState block_state(zone(), &scope_);
|
||||
scope()->set_start_position(scanner()->location().beg_pos);
|
||||
|
||||
@ -5791,8 +5639,6 @@ ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
|
||||
Scope* for_scope = scope();
|
||||
|
||||
{
|
||||
ReturnExprScope no_tail_calls(function_state_,
|
||||
ReturnExprContext::kInsideForInOfBody);
|
||||
BlockState block_state(zone(), &scope_);
|
||||
scope()->set_start_position(scanner()->location().beg_pos);
|
||||
|
||||
@ -5998,8 +5844,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
|
||||
StatementT final_loop = impl()->NullStatement();
|
||||
Scope* for_scope = scope();
|
||||
{
|
||||
ReturnExprScope no_tail_calls(function_state_,
|
||||
ReturnExprContext::kInsideForInOfBody);
|
||||
BlockState block_state(zone(), &scope_);
|
||||
scope()->set_start_position(scanner()->location().beg_pos);
|
||||
|
||||
|
@ -421,10 +421,6 @@ Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Parser::MarkTailPosition(Expression* expression) {
|
||||
expression->MarkTail();
|
||||
}
|
||||
|
||||
Expression* Parser::NewV8Intrinsic(const AstRawString* name,
|
||||
ZoneList<Expression*>* args, int pos,
|
||||
bool* ok) {
|
||||
@ -521,8 +517,6 @@ Parser::Parser(ParseInfo* info)
|
||||
allow_lazy_ = FLAG_lazy && info->allow_lazy_parsing() && !info->is_native() &&
|
||||
info->extension() == nullptr && can_compile_lazily;
|
||||
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
|
||||
set_allow_tailcalls(FLAG_harmony_tailcalls && !info->is_native() &&
|
||||
info->is_tail_call_elimination_enabled());
|
||||
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
|
||||
set_allow_harmony_function_sent(FLAG_harmony_function_sent);
|
||||
set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators);
|
||||
@ -1760,11 +1754,6 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
|
||||
}
|
||||
|
||||
if (catch_block != nullptr) {
|
||||
// For a try-catch construct append return expressions from the catch block
|
||||
// to the list of return expressions.
|
||||
function_state_->tail_call_expressions().Append(
|
||||
catch_info.tail_call_expressions);
|
||||
|
||||
DCHECK_NULL(finally_block);
|
||||
DCHECK_NOT_NULL(catch_info.scope);
|
||||
TryCatchStatement* stmt = factory()->NewTryCatchStatement(
|
||||
@ -3757,10 +3746,8 @@ ZoneList<Expression*>* Parser::PrepareSpreadArguments(
|
||||
Expression* Parser::SpreadCall(Expression* function,
|
||||
ZoneList<Expression*>* args, int pos,
|
||||
Call::PossiblyEval is_possibly_eval) {
|
||||
// Handle these cases in BytecodeGenerator.
|
||||
// [Call,New]WithSpread bytecodes aren't used with tailcalls - see
|
||||
// https://crbug.com/v8/5867
|
||||
if (!allow_tailcalls() && OnlyLastArgIsSpread(args)) {
|
||||
// Handle this case in BytecodeGenerator.
|
||||
if (OnlyLastArgIsSpread(args)) {
|
||||
return factory()->NewCall(function, args, pos);
|
||||
}
|
||||
|
||||
@ -3841,14 +3828,6 @@ void Parser::SetAsmModule() {
|
||||
scope()->AsDeclarationScope()->set_asm_module();
|
||||
}
|
||||
|
||||
void Parser::MarkCollectedTailCallExpressions() {
|
||||
const ZoneList<Expression*>& tail_call_expressions =
|
||||
function_state_->tail_call_expressions().expressions();
|
||||
for (int i = 0; i < tail_call_expressions.length(); ++i) {
|
||||
MarkTailPosition(tail_call_expressions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Expression* Parser::ExpressionListToExpression(ZoneList<Expression*>* args) {
|
||||
Expression* expr = args->at(0);
|
||||
for (int i = 1; i < args->length(); ++i) {
|
||||
|
@ -293,7 +293,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
|
||||
parsing_on_main_thread_);
|
||||
#define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name());
|
||||
SET_ALLOW(natives);
|
||||
SET_ALLOW(tailcalls);
|
||||
SET_ALLOW(harmony_do_expressions);
|
||||
SET_ALLOW(harmony_function_sent);
|
||||
SET_ALLOW(harmony_class_fields);
|
||||
@ -635,9 +634,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
|
||||
void SetLanguageMode(Scope* scope, LanguageMode mode);
|
||||
void SetAsmModule();
|
||||
|
||||
V8_INLINE void MarkCollectedTailCallExpressions();
|
||||
V8_INLINE void MarkTailPosition(Expression* expression);
|
||||
|
||||
// Rewrite all DestructuringAssignments in the current FunctionState.
|
||||
V8_INLINE void RewriteDestructuringAssignments();
|
||||
|
||||
|
@ -738,14 +738,6 @@ RUNTIME_FUNCTION(Runtime_TraceExit) {
|
||||
return obj; // return TOS
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TraceTailCall) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
PrintIndentation(isolate);
|
||||
PrintF("} -> tail call ->\n");
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetExceptionDetails) {
|
||||
HandleScope shs(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
|
@ -584,7 +584,6 @@ namespace internal {
|
||||
F(DisassembleFunction, 1, 1) \
|
||||
F(TraceEnter, 0, 1) \
|
||||
F(TraceExit, 1, 1) \
|
||||
F(TraceTailCall, 0, 1) \
|
||||
F(HaveSameMap, 2, 1) \
|
||||
F(InNewSpace, 1, 1) \
|
||||
F(HasSmiElements, 1, 1) \
|
||||
|
@ -1246,7 +1246,7 @@ TEST(InterpreterStoreKeyedProperty) {
|
||||
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
|
||||
}
|
||||
|
||||
static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
TEST(InterpreterCall) {
|
||||
HandleAndZoneScope handles;
|
||||
Isolate* isolate = handles.main_isolate();
|
||||
Zone* zone = handles.main_zone();
|
||||
@ -1275,11 +1275,7 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.MoveRegister(builder.Receiver(), args[0]);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
builder.TailCall(reg, args, call_slot_index);
|
||||
} else {
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
}
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
|
||||
builder.Return();
|
||||
ast_factory.Internalize(isolate);
|
||||
@ -1302,11 +1298,7 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
builder.LoadNamedProperty(builder.Receiver(), name, slot_index)
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.MoveRegister(builder.Receiver(), args[0]);
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
builder.TailCall(reg, args, call_slot_index);
|
||||
} else {
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
}
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
builder.Return();
|
||||
ast_factory.Internalize(isolate);
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
|
||||
@ -1338,11 +1330,7 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
.LoadLiteral(Smi::FromInt(11))
|
||||
.StoreAccumulatorInRegister(args[2]);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
builder.TailCall(reg, args, call_slot_index);
|
||||
} else {
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
}
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
|
||||
builder.Return();
|
||||
|
||||
@ -1391,11 +1379,7 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
.LoadLiteral(ast_factory.NewString(ast_factory.GetOneByteString("j")))
|
||||
.StoreAccumulatorInRegister(args[10]);
|
||||
|
||||
if (tail_call_mode == TailCallMode::kAllow) {
|
||||
builder.TailCall(reg, args, call_slot_index);
|
||||
} else {
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
}
|
||||
builder.CallProperty(reg, args, call_slot_index);
|
||||
|
||||
builder.Return();
|
||||
|
||||
@ -1419,10 +1403,6 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(InterpreterCall) { TestInterpreterCall(TailCallMode::kDisallow); }
|
||||
|
||||
TEST(InterpreterTailCall) { TestInterpreterCall(TailCallMode::kAllow); }
|
||||
|
||||
static BytecodeArrayBuilder& SetRegister(BytecodeArrayBuilder& builder,
|
||||
Register reg, int value,
|
||||
Register scratch) {
|
||||
|
@ -6431,123 +6431,6 @@ TEST(BreakLocationIterator) {
|
||||
DisableDebugger(isolate);
|
||||
}
|
||||
|
||||
TEST(DisableTailCallElimination) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
i::FLAG_harmony_tailcalls = true;
|
||||
// TODO(ishell, 4698): Investigate why TurboFan in --always-opt mode makes
|
||||
// stack[2].getFunctionName() return null.
|
||||
i::FLAG_turbo_inlining = false;
|
||||
|
||||
DebugLocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
CHECK(i_isolate->is_tail_call_elimination_enabled());
|
||||
|
||||
CompileRun(
|
||||
"'use strict'; \n"
|
||||
"Error.prepareStackTrace = (error,stack) => { \n"
|
||||
" error.strace = stack; \n"
|
||||
" return error.message + \"\\n at \" + stack.join(\"\\n at \"); \n"
|
||||
"} \n"
|
||||
" \n"
|
||||
"function getCaller() { \n"
|
||||
" var e = new Error(); \n"
|
||||
" e.stack; // prepare stack trace \n"
|
||||
" var stack = e.strace; \n"
|
||||
" %GlobalPrint('caller: '); \n"
|
||||
" %GlobalPrint(stack[2].getFunctionName()); \n"
|
||||
" %GlobalPrint('\\n'); \n"
|
||||
" return stack[2].getFunctionName(); \n"
|
||||
"} \n"
|
||||
"function f() { \n"
|
||||
" var caller = getCaller(); \n"
|
||||
" if (caller === 'g') return 1; \n"
|
||||
" if (caller === 'h') return 2; \n"
|
||||
" return 0; \n"
|
||||
"} \n"
|
||||
"function g() { \n"
|
||||
" return f(); \n"
|
||||
"} \n"
|
||||
"function h() { \n"
|
||||
" var result = g(); \n"
|
||||
" return result; \n"
|
||||
"} \n"
|
||||
"%NeverOptimizeFunction(getCaller); \n"
|
||||
"%NeverOptimizeFunction(f); \n"
|
||||
"%NeverOptimizeFunction(h); \n"
|
||||
"");
|
||||
ExpectInt32("h();", 2);
|
||||
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 2);
|
||||
i_isolate->SetTailCallEliminationEnabled(false);
|
||||
CHECK(!i_isolate->is_tail_call_elimination_enabled());
|
||||
ExpectInt32("h();", 1);
|
||||
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 1);
|
||||
i_isolate->SetTailCallEliminationEnabled(true);
|
||||
CHECK(i_isolate->is_tail_call_elimination_enabled());
|
||||
ExpectInt32("h();", 2);
|
||||
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 2);
|
||||
}
|
||||
|
||||
TEST(DebugStepNextTailCallEliminiation) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
i::FLAG_harmony_tailcalls = true;
|
||||
// TODO(ishell, 4698): Investigate why TurboFan in --always-opt mode makes
|
||||
// stack[2].getFunctionName() return null.
|
||||
i::FLAG_turbo_inlining = false;
|
||||
|
||||
DebugLocalContext env;
|
||||
env.ExposeDebug();
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
CHECK(i_isolate->is_tail_call_elimination_enabled());
|
||||
|
||||
const char* source =
|
||||
"'use strict'; \n"
|
||||
"var Debug = debug.Debug; \n"
|
||||
"var exception = null; \n"
|
||||
"var breaks = 0; \n"
|
||||
"var log = []; \n"
|
||||
"function f(x) { \n"
|
||||
" if (x == 2) { \n"
|
||||
" debugger; // Break a \n"
|
||||
" } \n"
|
||||
" if (x-- > 0) { // Break b \n"
|
||||
" return f(x); // Break c \n"
|
||||
" } \n"
|
||||
"} // Break e \n"
|
||||
"function listener(event, exec_state, event_data, data) {\n"
|
||||
" if (event != Debug.DebugEvent.Break) return; \n"
|
||||
" try { \n"
|
||||
" var line = exec_state.frame(0).sourceLineText(); \n"
|
||||
" var col = exec_state.frame(0).sourceColumn(); \n"
|
||||
" var match = line.match(/\\/\\/ Break (\\w)/); \n"
|
||||
" log.push(match[1] + col); \n"
|
||||
" exec_state.prepareStep(Debug.StepAction.StepNext); \n"
|
||||
" } catch (e) { \n"
|
||||
" exception = e; \n"
|
||||
" }; \n"
|
||||
"}; \n"
|
||||
"Debug.setListener(listener); \n"
|
||||
"f(4); \n"
|
||||
"Debug.setListener(null); // Break d \n";
|
||||
|
||||
CompileRun(source);
|
||||
ExpectNull("exception");
|
||||
ExpectString("JSON.stringify(log)", "[\"a4\",\"b2\",\"c4\",\"c11\",\"d0\"]");
|
||||
|
||||
i_isolate->SetTailCallEliminationEnabled(false);
|
||||
CompileRun(
|
||||
"log = []; \n"
|
||||
"Debug.setListener(listener); \n"
|
||||
"f(5); \n"
|
||||
"Debug.setListener(null); // Break f \n");
|
||||
ExpectNull("exception");
|
||||
ExpectString("JSON.stringify(log)",
|
||||
"[\"a4\",\"b2\",\"c4\",\"e0\",\"e0\",\"e0\",\"e0\",\"f0\"]");
|
||||
}
|
||||
|
||||
size_t current_action = 0;
|
||||
StepAction actions[] = {StepNext, StepNext};
|
||||
static void DebugStepOverFunctionWithCaughtExceptionListener(
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls
|
||||
|
||||
"use strict";
|
||||
|
||||
var Debug = debug.Debug
|
||||
var exception = null;
|
||||
var breaks = 0;
|
||||
|
||||
function f(x) {
|
||||
if (x > 0) { // B3 B5 B7 B9
|
||||
return f(x - 1); // B4 B6 B8
|
||||
}
|
||||
} // B10
|
||||
|
||||
function g(x) {
|
||||
return f(x); // B2
|
||||
}
|
||||
|
||||
function h(x) {
|
||||
debugger; // B0
|
||||
g(x); // B1
|
||||
} // B11
|
||||
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
try {
|
||||
print(event_data.sourceLineText());
|
||||
assertTrue(event_data.sourceLineText().indexOf(`B${breaks++}`) > 0);
|
||||
exec_state.prepareStep(Debug.StepAction.StepIn);
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
};
|
||||
};
|
||||
|
||||
Debug.setListener(listener);
|
||||
|
||||
h(3);
|
||||
|
||||
Debug.setListener(null); // B12
|
||||
assertNull(exception);
|
||||
assertEquals(13, breaks);
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls
|
||||
|
||||
"use strict";
|
||||
|
||||
var Debug = debug.Debug
|
||||
var exception = null;
|
||||
var breaks = 0;
|
||||
|
||||
function f(x) {
|
||||
if (x > 0) {
|
||||
return f(x - 1); // Tail call
|
||||
}
|
||||
debugger; // Break 0
|
||||
}
|
||||
|
||||
function g(x) {
|
||||
return f(x); // Tail call
|
||||
}
|
||||
|
||||
function h(x) {
|
||||
g(x); // Not tail call
|
||||
} // Break 1
|
||||
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
try {
|
||||
assertTrue(event_data.sourceLineText().indexOf(`Break ${breaks++}`) > 0);
|
||||
exec_state.prepareStep(Debug.StepAction.StepOut);
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
};
|
||||
};
|
||||
|
||||
Debug.setListener(listener);
|
||||
|
||||
h(3);
|
||||
|
||||
Debug.setListener(null); // Break 2
|
||||
assertNull(exception);
|
||||
assertEquals(3, breaks);
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls
|
||||
|
||||
"use strict";
|
||||
|
||||
function foo() {
|
||||
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(0);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(1);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(2);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(3);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(4);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(5);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(6);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(7);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(8);
|
@ -1,13 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
try {
|
||||
load("mjsunit/es6/tail-call-megatest.js");
|
||||
} catch(e) {
|
||||
load("test/mjsunit/es6/tail-call-megatest.js");
|
||||
}
|
||||
|
||||
run_tests(9);
|
@ -1,423 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
|
||||
|
||||
Error.prepareStackTrace = (error,stack) => {
|
||||
error.strace = stack;
|
||||
return error.message + "\n at " + stack.join("\n at ");
|
||||
}
|
||||
|
||||
var verbose = typeof(arguments) !== "undefined" && arguments.indexOf("-v") >= 0;
|
||||
|
||||
function checkStackTrace(expected) {
|
||||
var e = new Error();
|
||||
e.stack; // prepare stack trace
|
||||
var stack = e.strace;
|
||||
assertEquals("checkStackTrace", stack[0].getFunctionName());
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i].name, stack[i + 1].getFunctionName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var CAN_INLINE_COMMENT = "// Let it be inlined.";
|
||||
var DONT_INLINE_COMMENT = (function() {
|
||||
var line = "1";
|
||||
for (var i = 0; i < 200; ++i) {
|
||||
line += "," + i;
|
||||
}
|
||||
line += ";\n";
|
||||
return line;
|
||||
})();
|
||||
|
||||
|
||||
function ident_source(source, ident) {
|
||||
ident = " ".repeat(ident);
|
||||
return ident + source.replace(/\n/gi, "\n" + ident);
|
||||
}
|
||||
|
||||
var SHARDS_COUNT = 10;
|
||||
|
||||
function run_tests(shard) {
|
||||
function inlinable_comment(inlinable) {
|
||||
return inlinable ? CAN_INLINE_COMMENT : DONT_INLINE_COMMENT;
|
||||
}
|
||||
|
||||
// Check arguments manually to avoid bailing out with reason "bad value
|
||||
// context for arguments value".
|
||||
function check_arguments_template(expected_name) {
|
||||
var lines = [
|
||||
` assertEquals_(${expected_name}.length, arguments.length);`,
|
||||
` for (var i = 0; i < ${expected_name}.length; i++) {`,
|
||||
` assertEquals_(${expected_name}[i], arguments[i]);`,
|
||||
` }`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
}
|
||||
var check_arguments = check_arguments_template("expected_args");
|
||||
|
||||
function deopt_template(deopt_mode) {
|
||||
switch(deopt_mode) {
|
||||
case "none":
|
||||
return " // Don't deoptimize";
|
||||
case "f":
|
||||
case "g":
|
||||
case "test":
|
||||
return ` %DeoptimizeFunction(${deopt_mode});`;
|
||||
default:
|
||||
assertUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
var f_cfg_sloppy = {
|
||||
func_name: 'f',
|
||||
source_template: function(cfg) {
|
||||
var receiver = cfg.f_receiver != undefined ? cfg.f_receiver
|
||||
: "global";
|
||||
var do_checks = [
|
||||
` assertEquals_(${receiver}, this);`,
|
||||
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
|
||||
check_arguments,
|
||||
` checkStackTrace_([f, test]);`,
|
||||
].join("\n");
|
||||
|
||||
var lines = [
|
||||
`function f(a) {`,
|
||||
` ${inlinable_comment(cfg.f_inlinable)}`,
|
||||
` counter++;`,
|
||||
` var expected_args = [${cfg.f_args}];`,
|
||||
do_checks,
|
||||
deopt_template(cfg.deopt_mode),
|
||||
do_checks,
|
||||
` return 42;`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
var f_cfg_strict = {
|
||||
func_name: 'f',
|
||||
source_template: function(cfg) {
|
||||
var receiver = cfg.f_receiver != undefined ? cfg.f_receiver
|
||||
: "undefined";
|
||||
var do_checks = [
|
||||
` assertEquals_(${receiver}, this);`,
|
||||
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
|
||||
check_arguments,
|
||||
` checkStackTrace_([f, test]);`,
|
||||
].join("\n");
|
||||
|
||||
var lines = [
|
||||
`function f(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.f_inlinable)}`,
|
||||
` counter++;`,
|
||||
` var expected_args = [${cfg.f_args}];`,
|
||||
do_checks,
|
||||
deopt_template(cfg.deopt_mode),
|
||||
do_checks,
|
||||
` return 42;`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
var f_cfg_possibly_eval = {
|
||||
func_name: 'eval',
|
||||
source_template: function(cfg) {
|
||||
var receiver = cfg.f_receiver != undefined ? cfg.f_receiver
|
||||
: "global";
|
||||
var do_checks = [
|
||||
` assertEquals_(${receiver}, this);`,
|
||||
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
|
||||
check_arguments,
|
||||
` checkStackTrace_([f, test]);`,
|
||||
].join("\n");
|
||||
|
||||
var lines = [
|
||||
`function f(a) {`,
|
||||
` ${inlinable_comment(cfg.f_inlinable)}`,
|
||||
` counter++;`,
|
||||
` var expected_args = [${cfg.f_args}];`,
|
||||
do_checks,
|
||||
deopt_template(cfg.deopt_mode),
|
||||
do_checks,
|
||||
` return 42;`,
|
||||
`}`,
|
||||
`var eval = f;`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
var f_cfg_bound = {
|
||||
func_name: 'bound',
|
||||
source_template: function(cfg) {
|
||||
var do_checks = [
|
||||
` assertEquals_(receiver, this);`,
|
||||
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
|
||||
check_arguments,
|
||||
` checkStackTrace_([f, test]);`,
|
||||
].join("\n");
|
||||
|
||||
var lines = [
|
||||
`function f(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.f_inlinable)}`,
|
||||
` counter++;`,
|
||||
` var expected_args = [${cfg.f_args}];`,
|
||||
do_checks,
|
||||
deopt_template(cfg.deopt_mode),
|
||||
do_checks,
|
||||
` return 42;`,
|
||||
`}`,
|
||||
`var receiver = {a: 153};`,
|
||||
`var bound = f.bind(receiver);`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
var f_cfg_proxy = {
|
||||
func_name: 'p',
|
||||
source_template: function(cfg) {
|
||||
var receiver = cfg.f_receiver != undefined ? cfg.f_receiver
|
||||
: "global";
|
||||
var do_checks = [
|
||||
` assertEquals_(${receiver}, this);`,
|
||||
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
|
||||
check_arguments,
|
||||
` checkStackTrace_([f, test]);`,
|
||||
].join("\n");
|
||||
|
||||
var lines = [
|
||||
`function f(a) {`,
|
||||
` ${inlinable_comment(cfg.f_inlinable)}`,
|
||||
` counter++;`,
|
||||
` var expected_args = [${cfg.f_args}];`,
|
||||
do_checks,
|
||||
deopt_template(cfg.deopt_mode),
|
||||
do_checks,
|
||||
` return 42;`,
|
||||
`}`,
|
||||
`var p = new Proxy(f, {});`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
var g_cfg_normal = {
|
||||
receiver: undefined,
|
||||
source_template: function(cfg) {
|
||||
var lines = [
|
||||
`function g(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.g_inlinable)}`,
|
||||
` var expected_args = [${cfg.g_args}];`,
|
||||
check_arguments,
|
||||
` return ${cfg.f_name}(${cfg.f_args});`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var g_cfg_reflect_apply = {
|
||||
receiver: "the_receiver",
|
||||
source_template: function(cfg) {
|
||||
var lines = [
|
||||
`function g(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.g_inlinable)}`,
|
||||
` var expected_args = [${cfg.g_args}];`,
|
||||
check_arguments,
|
||||
` return Reflect.apply(${cfg.f_name}, the_receiver, [${cfg.f_args}]);`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var g_cfg_function_apply = {
|
||||
receiver: "the_receiver",
|
||||
source_template: function(cfg) {
|
||||
var lines = [
|
||||
`function g(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.g_inlinable)}`,
|
||||
` var expected_args = [${cfg.g_args}];`,
|
||||
check_arguments,
|
||||
` return ${cfg.f_name}.apply(the_receiver, [${cfg.f_args}]);`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var g_cfg_function_apply_arguments_object = {
|
||||
receiver: "the_receiver",
|
||||
source_template: function(cfg) {
|
||||
cfg.f_args = cfg.g_args;
|
||||
var lines = [
|
||||
`function g(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.g_inlinable)}`,
|
||||
` var expected_args = [${cfg.g_args}];`,
|
||||
check_arguments,
|
||||
` return ${cfg.f_name}.apply(the_receiver, arguments);`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var g_cfg_function_call = {
|
||||
receiver: "the_receiver",
|
||||
source_template: function(cfg) {
|
||||
var f_args = "the_receiver";
|
||||
if (cfg.f_args !== "") f_args += ", ";
|
||||
f_args += cfg.f_args;
|
||||
|
||||
var lines = [
|
||||
`function g(a) {`,
|
||||
` "use strict";`,
|
||||
` ${inlinable_comment(cfg.g_inlinable)}`,
|
||||
` var expected_args = [${cfg.g_args}];`,
|
||||
check_arguments,
|
||||
` return ${cfg.f_name}.call(${f_args});`,
|
||||
`}`,
|
||||
];
|
||||
return lines.join("\n");
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
function test_template(cfg) {
|
||||
// Note: g_source_template modifies cfg.f_args in some cases.
|
||||
var g_source = cfg.g_source_template(cfg);
|
||||
g_source = ident_source(g_source, 2);
|
||||
|
||||
var f_source = cfg.f_source_template(cfg);
|
||||
f_source = ident_source(f_source, 2);
|
||||
|
||||
var lines = [
|
||||
`(function() {`,
|
||||
` // Avoid bailing out because of "Reference to a variable which requires dynamic lookup".`,
|
||||
` var assertEquals_ = assertEquals;`,
|
||||
` var checkStackTrace_ = checkStackTrace;`,
|
||||
` var undefined = void 0;`,
|
||||
` var global = Function('return this')();`,
|
||||
` var the_receiver = {receiver: 1};`,
|
||||
` var counter = 0;`,
|
||||
``,
|
||||
` // Don't inline helper functions`,
|
||||
` %NeverOptimizeFunction(assertEquals);`,
|
||||
` %NeverOptimizeFunction(checkStackTrace);`,
|
||||
``,
|
||||
f_source,
|
||||
g_source,
|
||||
` function test() {`,
|
||||
` "use strict";`,
|
||||
` assertEquals_(42, g(${cfg.g_args}));`,
|
||||
` }`,
|
||||
` ${"test();".repeat(cfg.test_warmup_count)}`,
|
||||
` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : "%OptimizeFunctionOnNextCall(f)"};`,
|
||||
` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : "%OptimizeFunctionOnNextCall(g)"};`,
|
||||
` %OptimizeFunctionOnNextCall(test);`,
|
||||
` test();`,
|
||||
` assertEquals(${1 + cfg.test_warmup_count}, counter);`,
|
||||
`})();`,
|
||||
``,
|
||||
];
|
||||
var source = lines.join("\n");
|
||||
return source;
|
||||
}
|
||||
|
||||
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];
|
||||
// This is to avoid bailing out because of referencing new.target.
|
||||
var check_new_target_variants = [/*true,*/ false];
|
||||
var deopt_mode_variants = ["none", "f", "g", "test"];
|
||||
var f_variants = [
|
||||
f_cfg_sloppy,
|
||||
f_cfg_strict,
|
||||
f_cfg_bound,
|
||||
f_cfg_proxy,
|
||||
// f_cfg_possibly_eval,
|
||||
];
|
||||
var g_variants = [
|
||||
g_cfg_normal,
|
||||
// g_cfg_reflect_apply,
|
||||
g_cfg_function_apply,
|
||||
// g_cfg_function_apply_arguments_object,
|
||||
g_cfg_function_call,
|
||||
];
|
||||
var test_warmup_counts = [0, 1, 2];
|
||||
|
||||
var iter = 0;
|
||||
var tests_executed = 0;
|
||||
if (verbose && shard !== undefined) {
|
||||
print("Running shard #" + shard);
|
||||
}
|
||||
f_variants.forEach((f_cfg) => {
|
||||
check_new_target_variants.forEach((check_new_target) => {
|
||||
deopt_mode_variants.forEach((deopt_mode) => {
|
||||
g_variants.forEach((g_cfg) => {
|
||||
f_args_variants.forEach((f_args) => {
|
||||
g_args_variants.forEach((g_args) => {
|
||||
f_inlinable_variants.forEach((f_inlinable) => {
|
||||
g_inlinable_variants.forEach((g_inlinable) => {
|
||||
test_warmup_counts.forEach((test_warmup_count) => {
|
||||
if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) {
|
||||
if (verbose) {
|
||||
print("skipping...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
tests_executed++;
|
||||
var cfg = {
|
||||
f_source_template: f_cfg.source_template,
|
||||
f_inlinable,
|
||||
f_args,
|
||||
f_name: f_cfg.func_name,
|
||||
f_receiver: g_cfg.receiver,
|
||||
g_source_template: g_cfg.source_template,
|
||||
g_inlinable,
|
||||
g_args,
|
||||
test_warmup_count,
|
||||
check_new_target,
|
||||
deopt_mode,
|
||||
};
|
||||
var source = test_template(cfg);
|
||||
if (verbose) {
|
||||
// print("====================");
|
||||
// print(source);
|
||||
}
|
||||
eval(source);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
if (verbose) {
|
||||
print("Number of tests executed: " + tests_executed);
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to run all the tests at once or use shard runners.
|
||||
//run_tests();
|
@ -1,97 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
"use strict";
|
||||
|
||||
Error.prepareStackTrace = (e,s) => s;
|
||||
|
||||
function CheckStackTrace(expected) {
|
||||
var stack = (new Error()).stack;
|
||||
assertEquals("CheckStackTrace", stack[0].getFunctionName());
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i].name, stack[i + 1].getFunctionName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Tail call proxy function when caller does not have an arguments
|
||||
// adaptor frame.
|
||||
(function test() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p1 = new Proxy(f1, {});
|
||||
function g1(a) { return p1(2); }
|
||||
assertEquals(12, g1(1));
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p2 = new Proxy(f2, {});
|
||||
function g2(a, b, c) { return p2(2); }
|
||||
assertEquals(12, g2(1, 2, 3));
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
var p3 = new Proxy(f3, {});
|
||||
function g3(a) { return p3(2, 3, 4); }
|
||||
assertEquals(19, g3(1));
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p4 = new Proxy(f4, {});
|
||||
function g4(a) { return p4(2); }
|
||||
assertEquals(12, g4(1));
|
||||
})();
|
||||
|
||||
|
||||
// Tail call proxy function when caller has an arguments adaptor frame.
|
||||
(function test() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p1 = new Proxy(f1, {});
|
||||
function g1(a) { return p1(2); }
|
||||
assertEquals(12, g1());
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p2 = new Proxy(f2, {});
|
||||
function g2(a, b, c) { return p2(2); }
|
||||
assertEquals(12, g2());
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
var p3 = new Proxy(f3, {});
|
||||
function g3(a) { return p3(2, 3, 4); }
|
||||
assertEquals(19, g3());
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var p4 = new Proxy(f4, {});
|
||||
function g4(a) { return p4(2); }
|
||||
assertEquals(12, g4());
|
||||
})();
|
@ -1,143 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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 --stack-size=100
|
||||
|
||||
//
|
||||
// Tail calls work only in strict mode.
|
||||
//
|
||||
(function() {
|
||||
function f(n) {
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return f(n - 1);
|
||||
}
|
||||
assertThrows(()=>{ f(1e5) });
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertThrows(()=>{ f(1e5) });
|
||||
})();
|
||||
|
||||
|
||||
//
|
||||
// Tail call normal functions.
|
||||
//
|
||||
(function() {
|
||||
"use strict";
|
||||
function f(n) {
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return f(n - 1);
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
function f(n) {
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return f(n - 1, 42); // Call with arguments adaptor.
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
function f(n){
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return g(n - 1);
|
||||
}
|
||||
function g(n){
|
||||
if (n <= 0) {
|
||||
return "bar";
|
||||
}
|
||||
return f(n - 1);
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
function f(n){
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return g(n - 1, 42); // Call with arguments adaptor.
|
||||
}
|
||||
function g(n){
|
||||
if (n <= 0) {
|
||||
return "bar";
|
||||
}
|
||||
return f(n - 1, 42); // Call with arguments adaptor.
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
})();
|
||||
|
||||
|
||||
//
|
||||
// Tail call bound functions.
|
||||
//
|
||||
(function() {
|
||||
"use strict";
|
||||
function f0(n) {
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return f_bound(n - 1);
|
||||
}
|
||||
var f_bound = f0.bind({});
|
||||
function f(n) {
|
||||
return f_bound(n);
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
function f0(n){
|
||||
if (n <= 0) {
|
||||
return "foo";
|
||||
}
|
||||
return g_bound(n - 1);
|
||||
}
|
||||
function g0(n){
|
||||
if (n <= 0) {
|
||||
return "bar";
|
||||
}
|
||||
return f_bound(n - 1);
|
||||
}
|
||||
var f_bound = f0.bind({});
|
||||
var g_bound = g0.bind({});
|
||||
function f(n) {
|
||||
return f_bound(n);
|
||||
}
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals("foo", f(1e5));
|
||||
assertEquals("bar", f(1e5 + 1));
|
||||
})();
|
@ -1,663 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// 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
|
||||
// Flags: --harmony-do-expressions
|
||||
|
||||
"use strict";
|
||||
|
||||
Error.prepareStackTrace = (error,stack) => {
|
||||
error.strace = stack;
|
||||
return error.message + "\n at " + stack.join("\n at ");
|
||||
}
|
||||
|
||||
|
||||
function CheckStackTrace(expected) {
|
||||
var e = new Error();
|
||||
e.stack; // prepare stack trace
|
||||
var stack = e.strace;
|
||||
assertEquals("CheckStackTrace", stack[0].getFunctionName());
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i].name, stack[i + 1].getFunctionName());
|
||||
}
|
||||
}
|
||||
%NeverOptimizeFunction(CheckStackTrace);
|
||||
|
||||
|
||||
function f(expected_call_stack, a, b) {
|
||||
CheckStackTrace(expected_call_stack);
|
||||
return a;
|
||||
}
|
||||
|
||||
function f_153(expected_call_stack, a) {
|
||||
CheckStackTrace(expected_call_stack);
|
||||
return 153;
|
||||
}
|
||||
|
||||
|
||||
// Tail call when caller does not have an arguments adaptor frame.
|
||||
(function() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g1(a) { return f1(2); }
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g2(a, b, c) { return f2(2); }
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
function g3(a) { return f3(2, 3, 4); }
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g4(a) { return f4(2); }
|
||||
|
||||
function test() {
|
||||
assertEquals(12, g1(1));
|
||||
assertEquals(12, g2(1, 2, 3));
|
||||
assertEquals(19, g3(1));
|
||||
assertEquals(12, g4(1));
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail call when caller has an arguments adaptor frame.
|
||||
(function() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g1(a) { return f1(2); }
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g2(a, b, c) { return f2(2); }
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
function g3(a) { return f3(2, 3, 4); }
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
function g4(a) { return f4(2); }
|
||||
|
||||
function test() {
|
||||
assertEquals(12, g1());
|
||||
assertEquals(12, g2());
|
||||
assertEquals(19, g3());
|
||||
assertEquals(12, g4());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail call bound function when caller does not have an arguments
|
||||
// adaptor frame.
|
||||
(function() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b1 = f1.bind({a: 153});
|
||||
function g1(a) { return b1(2); }
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b2 = f2.bind({a: 153});
|
||||
function g2(a, b, c) { return b2(2); }
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
var b3 = f3.bind({a: 153});
|
||||
function g3(a) { return b3(2, 3, 4); }
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b4 = f4.bind({a: 153});
|
||||
function g4(a) { return b4(2); }
|
||||
|
||||
function test() {
|
||||
assertEquals(12, g1(1));
|
||||
assertEquals(12, g2(1, 2, 3));
|
||||
assertEquals(19, g3(1));
|
||||
assertEquals(12, g4(1));
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail call bound function when caller has an arguments adaptor frame.
|
||||
(function() {
|
||||
// Caller and callee have same number of arguments.
|
||||
function f1(a) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f1, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b1 = f1.bind({a: 153});
|
||||
function g1(a) { return b1(2); }
|
||||
|
||||
// Caller has more arguments than callee.
|
||||
function f2(a) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f2, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b2 = f2.bind({a: 153});
|
||||
function g2(a, b, c) { return b2(2); }
|
||||
|
||||
// Caller has less arguments than callee.
|
||||
function f3(a, b, c) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f3, test]);
|
||||
return 10 + a + b + c;
|
||||
}
|
||||
var b3 = f3.bind({a: 153});
|
||||
function g3(a) { return b3(2, 3, 4); }
|
||||
|
||||
// Callee has arguments adaptor frame.
|
||||
function f4(a, b, c) {
|
||||
assertEquals(153, this.a);
|
||||
CheckStackTrace([f4, test]);
|
||||
return 10 + a;
|
||||
}
|
||||
var b4 = f4.bind({a: 153});
|
||||
function g4(a) { return b4(2); }
|
||||
|
||||
function test() {
|
||||
assertEquals(12, g1());
|
||||
assertEquals(12, g2());
|
||||
assertEquals(19, g3());
|
||||
assertEquals(12, g4());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail calling from getter.
|
||||
(function() {
|
||||
function g(v) {
|
||||
CheckStackTrace([g, test]);
|
||||
%DeoptimizeFunction(test);
|
||||
return 153;
|
||||
}
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
function f(v) {
|
||||
return g();
|
||||
}
|
||||
%SetForceInlineFlag(f);
|
||||
|
||||
function test() {
|
||||
var o = {};
|
||||
o.__defineGetter__('p', f);
|
||||
assertEquals(153, o.p);
|
||||
}
|
||||
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail calling from setter.
|
||||
(function() {
|
||||
function g() {
|
||||
CheckStackTrace([g, test]);
|
||||
%DeoptimizeFunction(test);
|
||||
return 153;
|
||||
}
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
function f(v) {
|
||||
return g();
|
||||
}
|
||||
%SetForceInlineFlag(f);
|
||||
|
||||
function test() {
|
||||
var o = {};
|
||||
o.__defineSetter__('q', f);
|
||||
assertEquals(1, o.q = 1);
|
||||
}
|
||||
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail calling from constructor.
|
||||
(function() {
|
||||
function g(context) {
|
||||
CheckStackTrace([g, test]);
|
||||
%DeoptimizeFunction(test);
|
||||
return {x: 153};
|
||||
}
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
function A() {
|
||||
this.x = 42;
|
||||
return g();
|
||||
}
|
||||
|
||||
function test() {
|
||||
var o = new A();
|
||||
//%DebugPrint(o);
|
||||
assertEquals(153, o.x);
|
||||
}
|
||||
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail calling via various expressions.
|
||||
(function() {
|
||||
function g1(a) {
|
||||
return f([f, g1, test], false) || f([f, test], true);
|
||||
}
|
||||
|
||||
function g2(a) {
|
||||
return f([f, g2, test], true) && f([f, test], true);
|
||||
}
|
||||
|
||||
function g3(a) {
|
||||
return f([f, g3, test], 13), f([f, test], 153);
|
||||
}
|
||||
|
||||
function g4(a) {
|
||||
return f([f, g4, test], false) ||
|
||||
(f([f, g4, test], true) && f([f, test], true));
|
||||
}
|
||||
|
||||
function g5(a) {
|
||||
return f([f, g5, test], true) &&
|
||||
(f([f, g5, test], false) || f([f, test], true));
|
||||
}
|
||||
|
||||
function g6(a) {
|
||||
return f([f, g6, test], 13), f([f, g6, test], 42),
|
||||
f([f, test], 153);
|
||||
}
|
||||
|
||||
function g7(a) {
|
||||
return f([f, g7, test], false) ||
|
||||
(f([f, g7, test], false) ? f([f, test], true)
|
||||
: f([f, test], true));
|
||||
}
|
||||
|
||||
function g8(a) {
|
||||
return f([f, g8, test], false) || f([f, g8, test], true) &&
|
||||
f([f, test], true);
|
||||
}
|
||||
|
||||
function g9(a) {
|
||||
return f([f, g9, test], true) && f([f, g9, test], false) ||
|
||||
f([f, test], true);
|
||||
}
|
||||
|
||||
function g10(a) {
|
||||
return f([f, g10, test], true) && f([f, g10, test], false) ||
|
||||
f([f, g10, test], true) ?
|
||||
f([f, g10, test], true) && f([f, g10, test], false) ||
|
||||
f([f, test], true) :
|
||||
f([f, g10, test], true) && f([f, g10, test], false) ||
|
||||
f([f, test], true);
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(true, g1());
|
||||
assertEquals(true, g2());
|
||||
assertEquals(153, g3());
|
||||
assertEquals(true, g4());
|
||||
assertEquals(true, g5());
|
||||
assertEquals(153, g6());
|
||||
assertEquals(true, g7());
|
||||
assertEquals(true, g8());
|
||||
assertEquals(true, g9());
|
||||
assertEquals(true, g10());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Tail calling from various statements.
|
||||
(function() {
|
||||
function g1() {
|
||||
for (var v in {a:0}) {
|
||||
return f_153([f_153, g1, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g1let() {
|
||||
for (let v in {a:0}) {
|
||||
return f_153([f_153, g1let, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g1nodecl() {
|
||||
var v;
|
||||
for (v in {a:0}) {
|
||||
return f_153([f_153, g1nodecl, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g2() {
|
||||
for (var v of [1, 2, 3]) {
|
||||
return f_153([f_153, g2, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g2let() {
|
||||
for (let v of [1, 2, 3]) {
|
||||
return f_153([f_153, g2let, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g2nodecl() {
|
||||
var v;
|
||||
for (v of [1, 2, 3]) {
|
||||
return f_153([f_153, g2nodecl, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g3() {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g3let() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g3nodecl() {
|
||||
var i;
|
||||
for (i = 0; i < 10; i++) {
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g4() {
|
||||
while (true) {
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function g5() {
|
||||
do {
|
||||
return f_153([f_153, test]);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, g1());
|
||||
assertEquals(153, g1let());
|
||||
assertEquals(153, g1nodecl());
|
||||
assertEquals(153, g2());
|
||||
assertEquals(153, g2let());
|
||||
assertEquals(153, g2nodecl());
|
||||
assertEquals(153, g3());
|
||||
assertEquals(153, g3let());
|
||||
assertEquals(153, g3nodecl());
|
||||
assertEquals(153, g4());
|
||||
assertEquals(153, g5());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Test tail calls from try-catch constructs.
|
||||
(function() {
|
||||
function tc1(a) {
|
||||
try {
|
||||
f_153([f_153, tc1, test]);
|
||||
return f_153([f_153, tc1, test]);
|
||||
} catch(e) {
|
||||
f_153([f_153, tc1, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tc2(a) {
|
||||
try {
|
||||
f_153([f_153, tc2, test]);
|
||||
throw new Error("boom");
|
||||
} catch(e) {
|
||||
f_153([f_153, tc2, test]);
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tc3(a) {
|
||||
try {
|
||||
f_153([f_153, tc3, test]);
|
||||
throw new Error("boom");
|
||||
} catch(e) {
|
||||
f_153([f_153, tc3, test]);
|
||||
}
|
||||
f_153([f_153, tc3, test]);
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, tc1());
|
||||
assertEquals(153, tc2());
|
||||
assertEquals(153, tc3());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Test tail calls from try-finally constructs.
|
||||
(function() {
|
||||
function tf1(a) {
|
||||
try {
|
||||
f_153([f_153, tf1, test]);
|
||||
return f_153([f_153, tf1, test]);
|
||||
} finally {
|
||||
f_153([f_153, tf1, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tf2(a) {
|
||||
try {
|
||||
f_153([f_153, tf2, test]);
|
||||
throw new Error("boom");
|
||||
} finally {
|
||||
f_153([f_153, tf2, test]);
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tf3(a) {
|
||||
try {
|
||||
f_153([f_153, tf3, test]);
|
||||
} finally {
|
||||
f_153([f_153, tf3, test]);
|
||||
}
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, tf1());
|
||||
assertEquals(153, tf2());
|
||||
assertEquals(153, tf3());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Test tail calls from try-catch-finally constructs.
|
||||
(function() {
|
||||
function tcf1(a) {
|
||||
try {
|
||||
f_153([f_153, tcf1, test]);
|
||||
return f_153([f_153, tcf1, test]);
|
||||
} catch(e) {
|
||||
} finally {
|
||||
f_153([f_153, tcf1, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tcf2(a) {
|
||||
try {
|
||||
f_153([f_153, tcf2, test]);
|
||||
throw new Error("boom");
|
||||
} catch(e) {
|
||||
f_153([f_153, tcf2, test]);
|
||||
return f_153([f_153, tcf2, test]);
|
||||
} finally {
|
||||
f_153([f_153, tcf2, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tcf3(a) {
|
||||
try {
|
||||
f_153([f_153, tcf3, test]);
|
||||
throw new Error("boom");
|
||||
} catch(e) {
|
||||
f_153([f_153, tcf3, test]);
|
||||
} finally {
|
||||
f_153([f_153, tcf3, test]);
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
}
|
||||
|
||||
function tcf4(a) {
|
||||
try {
|
||||
f_153([f_153, tcf4, test]);
|
||||
throw new Error("boom");
|
||||
} catch(e) {
|
||||
f_153([f_153, tcf4, test]);
|
||||
} finally {
|
||||
f_153([f_153, tcf4, test]);
|
||||
}
|
||||
return f_153([f_153, test]);
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, tcf1());
|
||||
assertEquals(153, tcf2());
|
||||
assertEquals(153, tcf3());
|
||||
assertEquals(153, tcf4());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Test tail calls from arrow functions.
|
||||
(function () {
|
||||
function g1(a) {
|
||||
return (() => { return f_153([f_153, test]); })();
|
||||
}
|
||||
|
||||
function g2(a) {
|
||||
return (() => f_153([f_153, test]))();
|
||||
}
|
||||
|
||||
function g3(a) {
|
||||
var closure = () => f([f, closure, test], true)
|
||||
? f_153([f_153, test])
|
||||
: f_153([f_153, test]);
|
||||
return closure();
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, g1());
|
||||
assertEquals(153, g2());
|
||||
assertEquals(153, g3());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
||||
|
||||
|
||||
// Test tail calls from do expressions.
|
||||
(function () {
|
||||
function g1(a) {
|
||||
var a = do { return f_153([f_153, test]); 42; };
|
||||
return a;
|
||||
}
|
||||
|
||||
function test() {
|
||||
assertEquals(153, g1());
|
||||
}
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
})();
|
@ -137,7 +137,6 @@
|
||||
|
||||
# Slow tests.
|
||||
'copy-on-write-assert': [PASS, SLOW],
|
||||
'es6/tail-call-megatest*': [PASS, SLOW, FAST_VARIANTS, ['tsan', SKIP]],
|
||||
'es6/typedarray-construct-offset-not-smi': [PASS, SLOW],
|
||||
'harmony/regexp-property-script-extensions': [PASS, SLOW],
|
||||
'numops-fuzz-part*': [PASS, ['mode == debug', SLOW]],
|
||||
@ -565,9 +564,6 @@
|
||||
# Deopting uses just enough memory to make this one OOM.
|
||||
'regress/regress-3976': [SKIP],
|
||||
|
||||
# Too slow.
|
||||
'es6/tail-call-megatest*': [SKIP],
|
||||
|
||||
# Forced optimisation path tests.
|
||||
'shared-function-tier-up-turbo': [SKIP],
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls
|
||||
|
||||
"use strict";
|
||||
|
||||
function h(global) { return global.boom(); }
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls --allow-natives-syntax
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls --allow-natives-syntax
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
function h() {
|
||||
var res = g.arguments[0].x;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
function h() {
|
||||
var res = g.arguments;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-tailcalls --allow-natives-syntax
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
function f() {
|
||||
"use strict";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user