Index: src/crankshaft/x64/lithium-codegen-x64.cc |
diff --git a/src/crankshaft/x64/lithium-codegen-x64.cc b/src/crankshaft/x64/lithium-codegen-x64.cc |
index f8dd6662d315a27e1e2c2fecd00e9663627bd7b5..d9d11d65ed87a981c44b5155ad4dc2e0a15a4ce3 100644 |
--- a/src/crankshaft/x64/lithium-codegen-x64.cc |
+++ b/src/crankshaft/x64/lithium-codegen-x64.cc |
@@ -3147,13 +3147,23 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { |
// Invoke the function. |
__ bind(&invoke); |
+ |
+ InvokeFlag flag = CALL_FUNCTION; |
+ if (instr->hydrogen()->tail_call_mode() == TailCallMode::kAllow) { |
+ // TODO(ishell): drop current frame before pushing arguments to the stack. |
+ flag = JUMP_FUNCTION; |
+ ParameterCount actual(rax); |
+ // It is safe to use rbx, rcx and r8 as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) rbx (expected number of arguments) will be initialized below. |
+ PrepareForTailCall(actual, rbx, rcx, r8); |
+ } |
+ |
DCHECK(instr->HasPointerMap()); |
LPointerMap* pointers = instr->pointer_map(); |
- SafepointGenerator safepoint_generator( |
- this, pointers, Safepoint::kLazyDeopt); |
+ SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); |
ParameterCount actual(rax); |
- __ InvokeFunction(function, no_reg, actual, CALL_FUNCTION, |
- safepoint_generator); |
+ __ InvokeFunction(function, no_reg, actual, flag, safepoint_generator); |
} |
@@ -3192,10 +3202,9 @@ void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) { |
CallRuntime(Runtime::kDeclareGlobals, instr); |
} |
- |
void LCodeGen::CallKnownFunction(Handle<JSFunction> function, |
int formal_parameter_count, int arity, |
- LInstruction* instr) { |
+ bool is_tail_call, LInstruction* instr) { |
bool dont_adapt_arguments = |
formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel; |
bool can_invoke_directly = |
@@ -3212,23 +3221,36 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, |
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); |
__ Set(rax, arity); |
+ bool is_self_call = function.is_identical_to(info()->closure()); |
+ |
// Invoke function. |
- if (function.is_identical_to(info()->closure())) { |
- __ CallSelf(); |
+ if (is_self_call) { |
+ Handle<Code> self(reinterpret_cast<Code**>(__ CodeObject().location())); |
+ if (is_tail_call) { |
+ __ Jump(self, RelocInfo::CODE_TARGET); |
+ } else { |
+ __ Call(self, RelocInfo::CODE_TARGET); |
+ } |
} else { |
- __ Call(FieldOperand(function_reg, JSFunction::kCodeEntryOffset)); |
+ Operand target = FieldOperand(function_reg, JSFunction::kCodeEntryOffset); |
+ if (is_tail_call) { |
+ __ Jump(target); |
+ } else { |
+ __ Call(target); |
+ } |
} |
- // Set up deoptimization. |
- RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); |
+ if (!is_tail_call) { |
+ // Set up deoptimization. |
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); |
+ } |
} else { |
// We need to adapt arguments. |
- SafepointGenerator generator( |
- this, pointers, Safepoint::kLazyDeopt); |
- ParameterCount count(arity); |
+ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); |
+ ParameterCount actual(arity); |
ParameterCount expected(formal_parameter_count); |
- __ InvokeFunction(function_reg, no_reg, expected, count, CALL_FUNCTION, |
- generator); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(function_reg, no_reg, expected, actual, flag, generator); |
} |
} |
@@ -3628,22 +3650,76 @@ void LCodeGen::DoMathClz32(LMathClz32* instr) { |
__ Lzcntl(result, input); |
} |
+void LCodeGen::PrepareForTailCall(const ParameterCount& actual, |
+ Register scratch1, Register scratch2, |
+ Register scratch3) { |
+#if DEBUG |
+ if (actual.is_reg()) { |
+ DCHECK(!AreAliased(actual.reg(), scratch1, scratch2, scratch3)); |
+ } else { |
+ DCHECK(!AreAliased(scratch1, scratch2, scratch3)); |
+ } |
+#endif |
+ if (FLAG_code_comments) { |
+ if (actual.is_reg()) { |
+ Comment(";;; PrepareForTailCall, actual: %s {", actual.reg().ToString()); |
+ } else { |
+ Comment(";;; PrepareForTailCall, actual: %d {", actual.immediate()); |
+ } |
+ } |
+ |
+ // 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)); |
+ __ Cmp(Operand(scratch2, StandardFrameConstants::kContextOffset), |
+ Smi::FromInt(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(caller_args_count_reg, |
+ Immediate(info()->literal()->parameter_count())); |
+ |
+ __ bind(&formal_parameter_count_loaded); |
+ __ PrepareForTailCall(actual, caller_args_count_reg, scratch2, scratch3, |
+ ReturnAddressState::kNotOnStack); |
+ Comment(";;; }"); |
+} |
void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { |
+ HInvokeFunction* hinstr = instr->hydrogen(); |
DCHECK(ToRegister(instr->context()).is(rsi)); |
DCHECK(ToRegister(instr->function()).is(rdi)); |
DCHECK(instr->HasPointerMap()); |
- Handle<JSFunction> known_function = instr->hydrogen()->known_function(); |
+ bool is_tail_call = hinstr->tail_call_mode() == TailCallMode::kAllow; |
+ |
+ if (is_tail_call) { |
+ ParameterCount actual(instr->arity()); |
+ // It is safe to use rbx, rcx and r8 as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) rbx (expected number of arguments) will be initialized below. |
+ PrepareForTailCall(actual, rbx, rcx, r8); |
+ } |
+ |
+ Handle<JSFunction> known_function = hinstr->known_function(); |
if (known_function.is_null()) { |
LPointerMap* pointers = instr->pointer_map(); |
SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); |
- ParameterCount count(instr->arity()); |
- __ InvokeFunction(rdi, no_reg, count, CALL_FUNCTION, generator); |
+ ParameterCount actual(instr->arity()); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(rdi, no_reg, actual, flag, generator); |
} else { |
- CallKnownFunction(known_function, |
- instr->hydrogen()->formal_parameter_count(), |
- instr->arity(), instr); |
+ CallKnownFunction(known_function, hinstr->formal_parameter_count(), |
+ instr->arity(), is_tail_call, instr); |
} |
} |