Index: src/crankshaft/mips64/lithium-codegen-mips64.cc |
diff --git a/src/crankshaft/mips64/lithium-codegen-mips64.cc b/src/crankshaft/mips64/lithium-codegen-mips64.cc |
index 721605aca98fb88e763cf6a7e40b5d26bc808235..be5525718a265dc3a7e53ba00ad63070766a71cb 100644 |
--- a/src/crankshaft/mips64/lithium-codegen-mips64.cc |
+++ b/src/crankshaft/mips64/lithium-codegen-mips64.cc |
@@ -3314,15 +3314,24 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { |
__ dsll(scratch, length, kPointerSizeLog2); |
__ 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(a0); |
+ // It is safe to use t0, t1 and t2 as scratch registers here given that |
+ // we are not going to return to caller function anyway. |
+ PrepareForTailCall(actual, t0, t1, t2); |
+ } |
+ |
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 a0, as expected |
// by InvokeFunction. |
ParameterCount actual(receiver); |
- __ InvokeFunction(function, no_reg, actual, CALL_FUNCTION, |
- safepoint_generator); |
+ __ InvokeFunction(function, no_reg, actual, flag, safepoint_generator); |
} |
@@ -3368,10 +3377,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 = |
@@ -3388,17 +3396,35 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, |
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex); |
__ li(a0, Operand(arity)); |
+ bool is_self_call = function.is_identical_to(info()->closure()); |
+ |
// Invoke function. |
- __ ld(at, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); |
- __ Call(at); |
+ 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 { |
+ __ ld(at, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); |
+ if (is_tail_call) { |
+ __ Jump(at); |
+ } else { |
+ __ Call(at); |
+ } |
+ } |
- // 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); |
} |
} |
@@ -3751,22 +3777,74 @@ 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; |
+ __ ld(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
+ __ ld(scratch3, MemOperand(scratch2, StandardFrameConstants::kContextOffset)); |
+ __ Branch(&no_arguments_adaptor, ne, scratch3, |
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); |
+ |
+ // Drop current frame and load arguments count from arguments adaptor frame. |
+ __ mov(fp, scratch2); |
+ __ ld(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 |
+ __ li(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(a1)); |
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 t0, t1 and t2 as scratch registers here given that |
+ // we are not going to return to caller function anyway. |
+ PrepareForTailCall(actual, t0, t1, t2); |
+ } |
+ |
+ 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(a1, no_reg, count, CALL_FUNCTION, generator); |
+ ParameterCount actual(instr->arity()); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(a1, 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); |
} |
} |