Index: src/crankshaft/arm/lithium-codegen-arm.cc |
diff --git a/src/crankshaft/arm/lithium-codegen-arm.cc b/src/crankshaft/arm/lithium-codegen-arm.cc |
index 31a0472fd562563e249c62639032ccb35f1d928d..1c752ac7827ab30a135a942f22d87db8a63ba9ae 100644 |
--- a/src/crankshaft/arm/lithium-codegen-arm.cc |
+++ b/src/crankshaft/arm/lithium-codegen-arm.cc |
@@ -3195,15 +3195,25 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { |
__ b(ne, &loop); |
__ 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(r0); |
+ // It is safe to use r3, r4 and r5 as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) r3 (new.target) will be initialized below. |
+ PrepareForTailCall(actual, r3, r4, r5); |
+ } |
+ |
DCHECK(instr->HasPointerMap()); |
LPointerMap* pointers = instr->pointer_map(); |
- SafepointGenerator safepoint_generator( |
- this, pointers, Safepoint::kLazyDeopt); |
+ SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); |
// The number of arguments is stored in receiver which is r0, as expected |
// by InvokeFunction. |
ParameterCount actual(receiver); |
- __ InvokeFunction(function, no_reg, actual, CALL_FUNCTION, |
- safepoint_generator); |
+ __ InvokeFunction(function, no_reg, actual, flag, safepoint_generator); |
} |
@@ -3250,10 +3260,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 = |
@@ -3271,17 +3280,35 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, |
__ LoadRoot(r3, Heap::kUndefinedValueRootIndex); |
__ mov(r0, Operand(arity)); |
+ bool is_self_call = function.is_identical_to(info()->closure()); |
+ |
// Invoke function. |
- __ ldr(ip, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); |
- __ Call(ip); |
+ 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 { |
+ __ ldr(ip, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); |
+ if (is_tail_call) { |
+ __ Jump(ip); |
+ } else { |
+ __ Call(ip); |
+ } |
+ } |
- // Set up deoptimization. |
- RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); |
+ if (!is_tail_call) { |
+ // Set up deoptimization. |
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); |
+ } |
} else { |
SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); |
- ParameterCount count(arity); |
+ ParameterCount actual(arity); |
ParameterCount expected(formal_parameter_count); |
- __ InvokeFunction(function_reg, expected, count, CALL_FUNCTION, generator); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(function_reg, expected, actual, flag, generator); |
} |
} |
@@ -3568,22 +3595,76 @@ void LCodeGen::DoMathClz32(LMathClz32* instr) { |
__ clz(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; |
+ __ ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
+ __ ldr(scratch3, |
+ MemOperand(scratch2, StandardFrameConstants::kContextOffset)); |
+ __ cmp(scratch3, Operand(Smi::FromInt(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 |
+ __ mov(caller_args_count_reg, Operand(info()->literal()->parameter_count())); |
+ |
+ __ bind(&formal_parameter_count_loaded); |
+ __ PrepareForTailCall(actual, caller_args_count_reg, scratch2, scratch3); |
+ |
+ Comment(";;; }"); |
+} |
void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { |
+ HInvokeFunction* hinstr = instr->hydrogen(); |
DCHECK(ToRegister(instr->context()).is(cp)); |
DCHECK(ToRegister(instr->function()).is(r1)); |
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 r3, r4 and r5 as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) r3 (new.target) will be initialized below. |
+ PrepareForTailCall(actual, r3, r4, r5); |
+ } |
+ |
+ 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(r1, no_reg, count, CALL_FUNCTION, generator); |
+ ParameterCount actual(instr->arity()); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(r1, 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); |
} |
} |