From 826f67bec35e1e2def0650dfa443f2534589f7a3 Mon Sep 17 00:00:00 2001 From: mbrandy Date: Mon, 7 Mar 2016 12:55:03 -0800 Subject: [PATCH] PPC: [crankshaft] Support ES6 tail call elimination. Port 22938040fdb9a061babe96f004355fd79b2958a0 Original commit message: HInvokeFunction and HApplyArguments instructions now support tail calling. Inlining of calls at tail position is not supported yet and therefore still disabled. The tail-call-megatest was modified so that the usages of "arguments" object do not disable Crankshaft. R=ishell@chromium.org, joransiu@ca.ibm.com, jyan@ca.ibm.com, michael_dawson@ca.ibm.com BUG=v8:4698 LOG=N Review URL: https://codereview.chromium.org/1767173002 Cr-Commit-Position: refs/heads/master@{#34563} --- src/crankshaft/ppc/lithium-codegen-ppc.cc | 108 ++++++++++++++++++---- src/crankshaft/ppc/lithium-codegen-ppc.h | 5 +- src/crankshaft/ppc/lithium-ppc.h | 1 + src/ppc/builtins-ppc.cc | 58 +++--------- src/ppc/macro-assembler-ppc.cc | 61 ++++++++++++ src/ppc/macro-assembler-ppc.h | 15 +-- 6 files changed, 178 insertions(+), 70 deletions(-) diff --git a/src/crankshaft/ppc/lithium-codegen-ppc.cc b/src/crankshaft/ppc/lithium-codegen-ppc.cc index cf06108660..ffd32798d4 100644 --- a/src/crankshaft/ppc/lithium-codegen-ppc.cc +++ b/src/crankshaft/ppc/lithium-codegen-ppc.cc @@ -3348,14 +3348,25 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ bdnz(&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(r3); + // It is safe to use r6, r7 and r8 as scratch registers here given that + // 1) we are not going to return to caller function anyway, + // 2) r6 (new.target) will be initialized below. + PrepareForTailCall(actual, r6, r7, r8); + } + DCHECK(instr->HasPointerMap()); LPointerMap* pointers = instr->pointer_map(); SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); // The number of arguments is stored in receiver which is r3, as expected // by InvokeFunction. ParameterCount actual(receiver); - __ InvokeFunction(function, no_reg, actual, CALL_FUNCTION, - safepoint_generator); + __ InvokeFunction(function, no_reg, actual, flag, safepoint_generator); } @@ -3400,10 +3411,9 @@ void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) { CallRuntime(Runtime::kDeclareGlobals, instr); } - void LCodeGen::CallKnownFunction(Handle 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 = @@ -3425,19 +3435,31 @@ void LCodeGen::CallKnownFunction(Handle function, // Invoke function. if (is_self_call) { - __ CallSelf(); + Handle self(reinterpret_cast(__ CodeObject().location())); + if (is_tail_call) { + __ Jump(self, RelocInfo::CODE_TARGET); + } else { + __ Call(self, RelocInfo::CODE_TARGET); + } } else { __ LoadP(ip, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); - __ CallJSEntry(ip); + if (is_tail_call) { + __ JumpToJSEntry(ip); + } else { + __ CallJSEntry(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); } } @@ -3766,22 +3788,76 @@ void LCodeGen::DoMathClz32(LMathClz32* instr) { __ cntlzw_(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; + __ LoadP(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + __ LoadP(scratch3, + MemOperand(scratch2, StandardFrameConstants::kContextOffset)); + __ CmpSmiLiteral(scratch3, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0); + __ bne(&no_arguments_adaptor); + + // Drop current frame and load arguments count from arguments adaptor frame. + __ mr(fp, scratch2); + __ LoadP(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(r4)); DCHECK(instr->HasPointerMap()); - Handle 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 r6, r7 and r8 as scratch registers here given that + // 1) we are not going to return to caller function anyway, + // 2) r6 (new.target) will be initialized below. + PrepareForTailCall(actual, r6, r7, r8); + } + + Handle 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(r4, no_reg, count, CALL_FUNCTION, generator); + ParameterCount actual(instr->arity()); + InvokeFlag flag = is_tail_call ? JUMP_FUNCTION : CALL_FUNCTION; + __ InvokeFunction(r4, 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); } } diff --git a/src/crankshaft/ppc/lithium-codegen-ppc.h b/src/crankshaft/ppc/lithium-codegen-ppc.h index 1b72bf82dc..28f168036c 100644 --- a/src/crankshaft/ppc/lithium-codegen-ppc.h +++ b/src/crankshaft/ppc/lithium-codegen-ppc.h @@ -194,11 +194,14 @@ class LCodeGen : public LCodeGenBase { void CallRuntimeFromDeferred(Runtime::FunctionId id, int argc, LInstruction* instr, LOperand* context); + void PrepareForTailCall(const ParameterCount& actual, Register scratch1, + Register scratch2, Register scratch3); + // Generate a direct call to a known function. Expects the function // to be in r4. void CallKnownFunction(Handle function, int formal_parameter_count, int arity, - LInstruction* instr); + bool is_tail_call, LInstruction* instr); void RecordSafepointWithLazyDeopt(LInstruction* instr, SafepointMode safepoint_mode); diff --git a/src/crankshaft/ppc/lithium-ppc.h b/src/crankshaft/ppc/lithium-ppc.h index 31e149b6a6..58f6b4927a 100644 --- a/src/crankshaft/ppc/lithium-ppc.h +++ b/src/crankshaft/ppc/lithium-ppc.h @@ -523,6 +523,7 @@ class LApplyArguments final : public LTemplateInstruction<1, 4, 0> { } DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") + DECLARE_HYDROGEN_ACCESSOR(ApplyArguments) LOperand* function() { return inputs_[0]; } LOperand* receiver() { return inputs_[1]; } diff --git a/src/ppc/builtins-ppc.cc b/src/ppc/builtins-ppc.cc index f0b76ccc39..b11c707cad 100644 --- a/src/ppc/builtins-ppc.cc +++ b/src/ppc/builtins-ppc.cc @@ -2008,6 +2008,7 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg, } // Check if next frame is an arguments adaptor frame. + Register caller_args_count_reg = scratch1; Label no_arguments_adaptor, formal_parameter_count_loaded; __ LoadP(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ LoadP(scratch3, @@ -2015,11 +2016,11 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg, __ CmpSmiLiteral(scratch3, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0); __ bne(&no_arguments_adaptor); - // Drop arguments adaptor frame and load arguments count. + // Drop current frame and load arguments count from arguments adaptor frame. __ mr(fp, scratch2); - __ LoadP(scratch1, + __ LoadP(caller_args_count_reg, MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(scratch1); + __ SmiUntag(caller_args_count_reg); __ b(&formal_parameter_count_loaded); __ bind(&no_arguments_adaptor); @@ -2028,55 +2029,18 @@ void PrepareForTailCall(MacroAssembler* masm, Register args_reg, __ LoadP(scratch1, FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset)); __ LoadWordArith( - scratch1, FieldMemOperand( - scratch1, SharedFunctionInfo::kFormalParameterCountOffset)); + caller_args_count_reg, + FieldMemOperand(scratch1, + SharedFunctionInfo::kFormalParameterCountOffset)); #if !V8_TARGET_ARCH_PPC64 - __ SmiUntag(scratch1); + __ SmiUntag(caller_args_count_reg); #endif __ bind(&formal_parameter_count_loaded); - // Calculate the end of destination area where we will put the arguments - // after we drop current frame. We add kPointerSize to count the receiver - // argument which is not included into formal parameters count. - Register dst_reg = scratch2; - __ ShiftLeftImm(dst_reg, scratch1, Operand(kPointerSizeLog2)); - __ add(dst_reg, fp, dst_reg); - __ addi(dst_reg, dst_reg, - Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize)); - - Register src_reg = scratch1; - __ ShiftLeftImm(src_reg, args_reg, Operand(kPointerSizeLog2)); - __ add(src_reg, sp, src_reg); - // Count receiver argument as well (not included in args_reg). - __ addi(src_reg, src_reg, Operand(kPointerSize)); - - if (FLAG_debug_code) { - __ cmpl(src_reg, dst_reg); - __ Check(lt, kStackAccessBelowStackPointer); - } - - // Restore caller's frame pointer and return address now as they will be - // overwritten by the copying loop. - __ RestoreFrameStateForTailCall(); - - // Now copy callee arguments to the caller frame going backwards to avoid - // callee arguments corruption (source and destination areas could overlap). - - // Both src_reg and dst_reg are pointing to the word after the one to copy, - // so they must be pre-decremented in the loop. - Register tmp_reg = scratch3; - Label loop; - __ addi(tmp_reg, args_reg, Operand(1)); // +1 for receiver - __ mtctr(tmp_reg); - __ bind(&loop); - __ LoadPU(tmp_reg, MemOperand(src_reg, -kPointerSize)); - __ StorePU(tmp_reg, MemOperand(dst_reg, -kPointerSize)); - __ bdnz(&loop); - - // Leave current frame. - __ mr(sp, dst_reg); - + ParameterCount callee_args_count(args_reg); + __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, + scratch3); __ bind(&done); } } // namespace diff --git a/src/ppc/macro-assembler-ppc.cc b/src/ppc/macro-assembler-ppc.cc index 14759de0da..3f8640a408 100644 --- a/src/ppc/macro-assembler-ppc.cc +++ b/src/ppc/macro-assembler-ppc.cc @@ -1093,6 +1093,67 @@ void MacroAssembler::MovFromFloatParameter(const DoubleRegister dst) { Move(dst, d1); } +void MacroAssembler::PrepareForTailCall(const ParameterCount& callee_args_count, + Register caller_args_count_reg, + Register scratch0, Register scratch1) { +#if DEBUG + if (callee_args_count.is_reg()) { + DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0, + scratch1)); + } else { + DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1)); + } +#endif + + // Calculate the end of destination area where we will put the arguments + // after we drop current frame. We add kPointerSize to count the receiver + // argument which is not included into formal parameters count. + Register dst_reg = scratch0; + ShiftLeftImm(dst_reg, caller_args_count_reg, Operand(kPointerSizeLog2)); + add(dst_reg, fp, dst_reg); + addi(dst_reg, dst_reg, + Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize)); + + Register src_reg = caller_args_count_reg; + // Calculate the end of source area. +kPointerSize is for the receiver. + if (callee_args_count.is_reg()) { + ShiftLeftImm(src_reg, callee_args_count.reg(), Operand(kPointerSizeLog2)); + add(src_reg, sp, src_reg); + addi(src_reg, src_reg, Operand(kPointerSize)); + } else { + Add(src_reg, sp, (callee_args_count.immediate() + 1) * kPointerSize, r0); + } + + if (FLAG_debug_code) { + cmpl(src_reg, dst_reg); + Check(lt, kStackAccessBelowStackPointer); + } + + // Restore caller's frame pointer and return address now as they will be + // overwritten by the copying loop. + RestoreFrameStateForTailCall(); + + // Now copy callee arguments to the caller frame going backwards to avoid + // callee arguments corruption (source and destination areas could overlap). + + // Both src_reg and dst_reg are pointing to the word after the one to copy, + // so they must be pre-decremented in the loop. + Register tmp_reg = scratch1; + Label loop; + if (callee_args_count.is_reg()) { + addi(tmp_reg, callee_args_count.reg(), Operand(1)); // +1 for receiver + } else { + mov(tmp_reg, Operand(callee_args_count.immediate() + 1)); + } + mtctr(tmp_reg); + bind(&loop); + LoadPU(tmp_reg, MemOperand(src_reg, -kPointerSize)); + StorePU(tmp_reg, MemOperand(dst_reg, -kPointerSize)); + bdnz(&loop); + + // Leave current frame. + mr(sp, dst_reg); +} void MacroAssembler::InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Label* done, diff --git a/src/ppc/macro-assembler-ppc.h b/src/ppc/macro-assembler-ppc.h index d9dbd56827..8cd9cf8c20 100644 --- a/src/ppc/macro-assembler-ppc.h +++ b/src/ppc/macro-assembler-ppc.h @@ -156,12 +156,6 @@ class MacroAssembler : public Assembler { void Call(Label* target); - // Emit call to the code we are currently generating. - void CallSelf() { - Handle self(reinterpret_cast(CodeObject().location())); - Call(self, RelocInfo::CODE_TARGET); - } - // Register move. May do nothing if the registers are identical. void Move(Register dst, Smi* smi) { LoadSmiLiteral(dst, smi); } void Move(Register dst, Handle value); @@ -564,6 +558,15 @@ class MacroAssembler : public Assembler { // --------------------------------------------------------------------------- // JavaScript invokes + // Removes current frame and its arguments from the stack preserving + // the arguments and a return address pushed to the stack for the next call. + // Both |callee_args_count| and |caller_args_count_reg| do not include + // receiver. |callee_args_count| is not modified, |caller_args_count_reg| + // is trashed. + void PrepareForTailCall(const ParameterCount& callee_args_count, + Register caller_args_count_reg, Register scratch0, + Register scratch1); + // Invoke the JavaScript function code by either calling or jumping. void InvokeFunctionCode(Register function, Register new_target, const ParameterCount& expected,