| 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);
|
| }
|
| }
|
|
|
|
|