Index: src/crankshaft/ia32/lithium-codegen-ia32.cc |
diff --git a/src/crankshaft/ia32/lithium-codegen-ia32.cc b/src/crankshaft/ia32/lithium-codegen-ia32.cc |
index 2ba7b3fa0ac913d8d8b80c8f84858deddbd22a61..ea3875da3898158963aef4f8ba2633046db41f08 100644 |
--- a/src/crankshaft/ia32/lithium-codegen-ia32.cc |
+++ b/src/crankshaft/ia32/lithium-codegen-ia32.cc |
@@ -2941,13 +2941,24 @@ 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(eax); |
+ // It is safe to use ebx, ecx and edx as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) ebx (expected arguments count) and edx (new.target) will be |
+ // initialized below. |
+ PrepareForTailCall(actual, ebx, ecx, edx); |
+ } |
+ |
DCHECK(instr->HasPointerMap()); |
LPointerMap* pointers = instr->pointer_map(); |
- SafepointGenerator safepoint_generator( |
- this, pointers, Safepoint::kLazyDeopt); |
+ SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); |
ParameterCount actual(eax); |
- __ InvokeFunction(function, no_reg, actual, CALL_FUNCTION, |
- safepoint_generator); |
+ __ InvokeFunction(function, no_reg, actual, flag, safepoint_generator); |
} |
@@ -2991,10 +3002,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 = |
@@ -3010,21 +3020,38 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, |
__ mov(edx, factory()->undefined_value()); |
__ mov(eax, arity); |
+ bool is_self_call = function.is_identical_to(info()->closure()); |
+ |
// Invoke function directly. |
- 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) { |
+ __ jmp(target); |
+ } else { |
+ __ call(target); |
+ } |
+ } |
+ |
+ if (!is_tail_call) { |
+ // Set up deoptimization. |
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); |
} |
- RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); |
} else { |
// We need to adapt arguments. |
LPointerMap* pointers = instr->pointer_map(); |
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); |
} |
} |
@@ -3416,23 +3443,77 @@ void LCodeGen::DoMathExp(LMathExp* instr) { |
MathExpGenerator::EmitMathExp(masm(), input, result, temp0, temp1, temp2); |
} |
+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; |
+ __ mov(scratch2, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); |
+ __ cmp(Operand(scratch2, StandardFrameConstants::kContextOffset), |
+ Immediate(Smi::FromInt(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(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(esi)); |
DCHECK(ToRegister(instr->function()).is(edi)); |
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 ebx, ecx and edx as scratch registers here given that |
+ // 1) we are not going to return to caller function anyway, |
+ // 2) ebx (expected arguments count) and edx (new.target) will be |
+ // initialized below. |
+ PrepareForTailCall(actual, ebx, ecx, edx); |
+ } |
+ |
+ 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(edi, no_reg, count, CALL_FUNCTION, generator); |
+ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); |
+ ParameterCount actual(instr->arity()); |
+ InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; |
+ __ InvokeFunction(edi, 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); |
} |
} |