[turbofan] Further fixing ES6 tail call elimination in Turbofan.

In case when F tail calls G we should also remove the potential arguments adaptor frame for F.

This CL introduces two new machine instructions ArchTailCallCodeObjectFromJSFunction and ArchTailCallJSFunctionFromJSFunction which (unlike existing ArchTailCallCodeObject and ArchTailCallJSFunction) also drop arguments adaptor frame if it exists right before jumping to the target function.

BUG=v8:4698
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#34566}
This commit is contained in:
ishell 2016-03-07 16:11:57 -08:00 committed by Commit bot
parent 56157fe819
commit 2aae579cf0
23 changed files with 350 additions and 71 deletions

View File

@ -405,14 +405,39 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ ldr(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ b(ne, &done);
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ ldr(caller_args_count_reg,
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
ArmOperandConverter i(this, instr);
__ MaybeCheckConstPool();
switch (ArchOpcodeField::decode(instr->opcode())) {
InstructionCode opcode = instr->opcode();
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
EnsureSpaceForLazyDeopt();
if (instr->InputAt(0)->IsImmediate()) {
@ -428,9 +453,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
@ -459,6 +490,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -469,6 +501,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
__ ldr(ip, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(ip);
DCHECK_EQ(LeaveCC, i.OutputSBit());

View File

@ -1302,6 +1302,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
namespace {

View File

@ -489,6 +489,30 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ Ldr(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ Cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ B(ne, &done);
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ Ldr(caller_args_count_reg,
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
@ -518,9 +542,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
RecordCallPosition(instr);
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
@ -557,6 +587,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
RecordCallPosition(instr);
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -569,6 +600,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
__ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(x10);
frame_access_state()->ClearSPDelta();

View File

@ -1667,6 +1667,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
namespace {

View File

@ -116,6 +116,10 @@ class CodeGenerator final : public GapResolver::Assembler {
// Generates code to manipulate the stack in preparation for a tail call.
void AssemblePrepareTailCall(int stack_param_delta);
// Generates code to pop current frame if it is an arguments adaptor frame.
void AssemblePopArgumentsAdaptorFrame(Register args_reg, Register scratch1,
Register scratch2, Register scratch3);
// ===========================================================================
// ============== Architecture-specific gap resolver methods. ================
// ===========================================================================

View File

@ -355,12 +355,50 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register, Register,
Register) {
// There are not enough temp registers left on ia32 for a call instruction
// so we pick some scratch registers and save/restore them manually here.
int scratch_count = 3;
Register scratch1 = ebx;
Register scratch2 = ecx;
Register scratch3 = edx;
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ cmp(Operand(ebp, StandardFrameConstants::kContextOffset),
Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ j(not_equal, &done, Label::kNear);
__ push(scratch1);
__ push(scratch2);
__ push(scratch3);
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ mov(caller_args_count_reg,
Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3, ReturnAddressState::kOnStack, scratch_count);
__ pop(scratch3);
__ pop(scratch2);
__ pop(scratch1);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
IA32OperandConverter i(this, instr);
switch (ArchOpcodeField::decode(instr->opcode())) {
InstructionCode opcode = instr->opcode();
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
EnsureSpaceForLazyDeopt();
if (HasImmediateInput(instr, 0)) {
@ -375,9 +413,14 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
no_reg, no_reg, no_reg);
}
if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET);
@ -402,6 +445,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -411,6 +455,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
no_reg, no_reg, no_reg);
}
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
frame_access_state()->ClearSPDelta();
break;

View File

@ -1013,6 +1013,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 0; }
namespace {

View File

@ -41,40 +41,42 @@ enum class RecordWriteMode { kValueIsMap, kValueIsPointer, kValueIsAny };
// Target-specific opcodes that specify which assembly sequence to emit.
// Most opcodes specify a single instruction.
#define COMMON_ARCH_OPCODE_LIST(V) \
V(ArchCallCodeObject) \
V(ArchTailCallCodeObject) \
V(ArchCallJSFunction) \
V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchPrepareTailCall) \
V(ArchJmp) \
V(ArchLookupSwitch) \
V(ArchTableSwitch) \
V(ArchNop) \
V(ArchThrowTerminator) \
V(ArchDeoptimize) \
V(ArchRet) \
V(ArchStackPointer) \
V(ArchFramePointer) \
V(ArchParentFramePointer) \
V(ArchTruncateDoubleToI) \
V(ArchStoreWithWriteBarrier) \
V(CheckedLoadInt8) \
V(CheckedLoadUint8) \
V(CheckedLoadInt16) \
V(CheckedLoadUint16) \
V(CheckedLoadWord32) \
V(CheckedLoadWord64) \
V(CheckedLoadFloat32) \
V(CheckedLoadFloat64) \
V(CheckedStoreWord8) \
V(CheckedStoreWord16) \
V(CheckedStoreWord32) \
V(CheckedStoreWord64) \
V(CheckedStoreFloat32) \
V(CheckedStoreFloat64) \
#define COMMON_ARCH_OPCODE_LIST(V) \
V(ArchCallCodeObject) \
V(ArchTailCallCodeObjectFromJSFunction) \
V(ArchTailCallCodeObject) \
V(ArchCallJSFunction) \
V(ArchTailCallJSFunctionFromJSFunction) \
V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchPrepareTailCall) \
V(ArchJmp) \
V(ArchLookupSwitch) \
V(ArchTableSwitch) \
V(ArchNop) \
V(ArchThrowTerminator) \
V(ArchDeoptimize) \
V(ArchRet) \
V(ArchStackPointer) \
V(ArchFramePointer) \
V(ArchParentFramePointer) \
V(ArchTruncateDoubleToI) \
V(ArchStoreWithWriteBarrier) \
V(CheckedLoadInt8) \
V(CheckedLoadUint8) \
V(CheckedLoadInt16) \
V(CheckedLoadUint16) \
V(CheckedLoadWord32) \
V(CheckedLoadWord64) \
V(CheckedLoadFloat32) \
V(CheckedLoadFloat64) \
V(CheckedStoreWord8) \
V(CheckedStoreWord16) \
V(CheckedStoreWord32) \
V(CheckedStoreWord64) \
V(CheckedStoreFloat32) \
V(CheckedStoreFloat64) \
V(ArchStackSlot)
#define ARCH_OPCODE_LIST(V) \

View File

@ -220,7 +220,9 @@ int InstructionScheduler::GetInstructionFlags(const Instruction* instr) const {
case kArchCallJSFunction:
return kHasSideEffect;
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject:
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction:
return kHasSideEffect | kIsBlockTerminator;

View File

@ -1570,16 +1570,35 @@ void InstructionSelector::VisitTailCall(Node* node) {
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject:
opcode = kArchTailCallCodeObject;
break;
case CallDescriptor::kCallJSFunction:
opcode = kArchTailCallJSFunction;
break;
default:
UNREACHABLE();
return;
InstructionOperandVector temps(zone());
if (linkage()->GetIncomingDescriptor()->IsJSFunctionCall()) {
switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject:
opcode = kArchTailCallCodeObjectFromJSFunction;
break;
case CallDescriptor::kCallJSFunction:
opcode = kArchTailCallJSFunctionFromJSFunction;
break;
default:
UNREACHABLE();
return;
}
int temps_count = GetTempsCountForTailCallFromJSFunction();
for (int i = 0; i < temps_count; i++) {
temps.push_back(g.TempRegister());
}
} else {
switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject:
opcode = kArchTailCallCodeObject;
break;
case CallDescriptor::kCallJSFunction:
opcode = kArchTailCallJSFunction;
break;
default:
UNREACHABLE();
return;
}
}
opcode |= MiscField::encode(descriptor->flags());
@ -1590,7 +1609,8 @@ void InstructionSelector::VisitTailCall(Node* node) {
// Emit the tailcall instruction.
Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
&buffer.instruction_args.front());
&buffer.instruction_args.front(), temps.size(),
temps.empty() ? nullptr : &temps.front());
} else {
FrameStateDescriptor* frame_state_descriptor =
descriptor->NeedsFrameState()

View File

@ -224,6 +224,7 @@ class InstructionSelector final {
void InitializeCallBuffer(Node* call, CallBuffer* buffer,
CallBufferFlags flags, int stack_param_delta = 0);
bool IsTailCallAddressImmediate();
int GetTempsCountForTailCallFromJSFunction();
FrameStateDescriptor* GetFrameStateDescriptor(Node* node);

View File

@ -489,13 +489,37 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ lw(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ Branch(&done, ne, scratch1,
Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ lw(caller_args_count_reg,
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
MipsOperandConverter i(this, instr);
InstructionCode opcode = instr->opcode();
switch (ArchOpcodeField::decode(opcode)) {
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
EnsureSpaceForLazyDeopt();
if (instr->InputAt(0)->IsImmediate()) {
@ -509,9 +533,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
@ -537,6 +567,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -547,6 +578,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
__ lw(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(at);
frame_access_state()->ClearSPDelta();

View File

@ -884,6 +884,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
void InstructionSelector::VisitCheckedLoad(Node* node) {
CheckedLoadRepresentation load_rep = CheckedLoadRepresentationOf(node->op());

View File

@ -501,13 +501,37 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ ld(scratch3, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ Branch(&done, ne, scratch3,
Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ ld(caller_args_count_reg,
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
MipsOperandConverter i(this, instr);
InstructionCode opcode = instr->opcode();
switch (ArchOpcodeField::decode(opcode)) {
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
EnsureSpaceForLazyDeopt();
if (instr->InputAt(0)->IsImmediate()) {
@ -521,9 +545,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
@ -548,6 +578,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -557,6 +588,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
__ ld(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(at);
frame_access_state()->ClearSPDelta();

View File

@ -1325,6 +1325,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
void InstructionSelector::VisitCheckedLoad(Node* node) {
CheckedLoadRepresentation load_rep = CheckedLoadRepresentationOf(node->op());

View File

@ -621,12 +621,37 @@ void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ Cmp(Operand(rbp, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(not_equal, &done, Label::kNear);
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ SmiToInteger32(
caller_args_count_reg,
Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3, ReturnAddressState::kOnStack);
__ bind(&done);
}
// Assembles an instruction after register allocation, producing machine code.
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
X64OperandConverter i(this, instr);
switch (ArchOpcodeField::decode(instr->opcode())) {
InstructionCode opcode = instr->opcode();
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
EnsureSpaceForLazyDeopt();
if (HasImmediateInput(instr, 0)) {
@ -641,9 +666,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET);
@ -668,6 +699,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
RecordCallPosition(instr);
break;
}
case kArchTailCallJSFunctionFromJSFunction:
case kArchTailCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
@ -677,6 +709,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (arch_opcode == kArchTailCallJSFunctionFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
frame_access_state()->ClearSPDelta();
break;

View File

@ -1356,6 +1356,7 @@ void InstructionSelector::EmitPrepareArguments(
bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
namespace {

View File

@ -3476,7 +3476,7 @@ void LCodeGen::PrepareForTailCall(const ParameterCount& actual,
__ bind(&formal_parameter_count_loaded);
__ PrepareForTailCall(actual, caller_args_count_reg, scratch2, scratch3,
ReturnAddressState::kNotOnStack);
ReturnAddressState::kNotOnStack, 0);
Comment(";;; }");
}

View File

@ -1909,7 +1909,7 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3, ReturnAddressState::kOnStack);
scratch3, ReturnAddressState::kOnStack, 0);
__ bind(&done);
}
} // namespace

View File

@ -2106,10 +2106,10 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) {
jmp(ces.GetCode(), RelocInfo::CODE_TARGET);
}
void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
Register caller_args_count_reg,
Register scratch0, Register scratch1,
ReturnAddressState ra_state) {
void MacroAssembler::PrepareForTailCall(
const ParameterCount& callee_args_count, Register caller_args_count_reg,
Register scratch0, Register scratch1, ReturnAddressState ra_state,
int number_of_temp_values_after_return_address) {
#if DEBUG
if (callee_args_count.is_reg()) {
DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0,
@ -2117,6 +2117,8 @@ void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
} else {
DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1));
}
DCHECK(ra_state != ReturnAddressState::kNotOnStack ||
number_of_temp_values_after_return_address == 0);
#endif
// Calculate the destination address where we will put the return address
@ -2124,12 +2126,16 @@ void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
Register new_sp_reg = scratch0;
if (callee_args_count.is_reg()) {
sub(caller_args_count_reg, callee_args_count.reg());
lea(new_sp_reg, Operand(ebp, caller_args_count_reg, times_pointer_size,
StandardFrameConstants::kCallerPCOffset));
lea(new_sp_reg,
Operand(ebp, caller_args_count_reg, times_pointer_size,
StandardFrameConstants::kCallerPCOffset -
number_of_temp_values_after_return_address * kPointerSize));
} else {
lea(new_sp_reg, Operand(ebp, caller_args_count_reg, times_pointer_size,
StandardFrameConstants::kCallerPCOffset -
callee_args_count.immediate() * kPointerSize));
(callee_args_count.immediate() +
number_of_temp_values_after_return_address) *
kPointerSize));
}
if (FLAG_debug_code) {
@ -2143,9 +2149,11 @@ void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
Register tmp_reg = scratch1;
if (ra_state == ReturnAddressState::kOnStack) {
mov(tmp_reg, Operand(ebp, StandardFrameConstants::kCallerPCOffset));
mov(Operand(esp, 0), tmp_reg);
mov(Operand(esp, number_of_temp_values_after_return_address * kPointerSize),
tmp_reg);
} else {
DCHECK(ReturnAddressState::kNotOnStack == ra_state);
DCHECK_EQ(0, number_of_temp_values_after_return_address);
Push(Operand(ebp, StandardFrameConstants::kCallerPCOffset));
}
@ -2156,9 +2164,11 @@ void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
// +2 here is to copy both receiver and return address.
Register count_reg = caller_args_count_reg;
if (callee_args_count.is_reg()) {
lea(count_reg, Operand(callee_args_count.reg(), 2));
lea(count_reg, Operand(callee_args_count.reg(),
2 + number_of_temp_values_after_return_address));
} else {
mov(count_reg, Immediate(callee_args_count.immediate() + 2));
mov(count_reg, Immediate(callee_args_count.immediate() + 2 +
number_of_temp_values_after_return_address));
// TODO(ishell): Unroll copying loop for small immediate values.
}

View File

@ -330,10 +330,14 @@ class MacroAssembler: public Assembler {
// |ra_state| defines whether return address is already pushed to stack or
// not. Both |callee_args_count| and |caller_args_count_reg| do not include
// receiver. |callee_args_count| is not modified, |caller_args_count_reg|
// is trashed.
// is trashed. |number_of_temp_values_after_return_address| specifies
// the number of words pushed to the stack after the return address. This is
// to allow "allocation" of scratch registers that this function requires
// by saving their values on the stack.
void PrepareForTailCall(const ParameterCount& callee_args_count,
Register caller_args_count_reg, Register scratch0,
Register scratch1, ReturnAddressState ra_state);
Register scratch1, ReturnAddressState ra_state,
int number_of_temp_values_after_return_address);
// Invoke the JavaScript function code by either calling or jumping.

View File

@ -4,7 +4,7 @@
// Flags: --allow-natives-syntax --harmony-tailcalls --no-turbo-inlining
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --no-turbo --nostress-opt
// Flags: --nostress-opt
Error.prepareStackTrace = (error,stack) => {

View File

@ -4,7 +4,7 @@
// Flags: --allow-natives-syntax --harmony-tailcalls --stack-size=100
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --no-turbo --nostress-opt
// Flags: --nostress-opt
//
// Tail calls work only in strict mode.