Commit c6d310da authored by danno's avatar danno Committed by Commit bot

[turbofan]: Implement tail calls with more callee than caller parameters

* Adds a PrepareForTailCall instruction that bumps the stack in the case that
  the number of parameters passed to the callee causes the stack to exceed the
  calleer's frame size.
* Uses the gap resolver to move the saved caller return address and frame
  pointer to the approprate location in the tail-called frame.

BUG=v8:4076
LOG=n

Review URL: https://codereview.chromium.org/1455833004

Cr-Commit-Position: refs/heads/master@{#32151}
parent cded3ea6
......@@ -355,14 +355,27 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ add(sp, sp, Operand(sp_slot_delta * kPointerSize));
}
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ LeaveFrame(StackFrame::MANUAL);
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
if (FLAG_enable_embedded_constant_pool) {
__ ldm(ia_w, sp, pp.bit() | fp.bit() | lr.bit());
} else {
__ ldm(ia_w, sp, fp.bit() | lr.bit());
}
}
if (stack_param_delta < 0) {
int offset = -stack_param_delta * kPointerSize;
__ add(sp, sp, Operand(offset));
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ sub(sp, sp, Operand(-sp_slot_delta * kPointerSize));
}
}
......@@ -442,6 +455,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
......
......@@ -458,15 +458,24 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ Add(jssp, jssp, Operand(sp_slot_delta * kPointerSize));
}
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ Mov(jssp, fp);
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
__ Pop(fp, lr);
}
if (stack_param_delta < 0) {
int offset = -stack_param_delta * kPointerSize;
__ Add(jssp, jssp, Operand(offset));
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ Sub(jssp, jssp, Operand(-sp_slot_delta * kPointerSize));
frame()->AllocateOutgoingParameterSlots(-sp_slot_delta);
}
}
......@@ -549,6 +558,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
// guarantee correct alignment of stack pointer.
UNREACHABLE();
break;
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
......
......@@ -669,6 +669,22 @@ void CodeGenerator::MarkLazyDeoptSite() {
}
int CodeGenerator::TailCallFrameStackSlotDelta(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
// Leave the PC and saved frame pointer on the stack.
int sp_slot_delta =
has_frame
? (frame()->GetTotalFrameSlotCount() -
(StandardFrameConstants::kFixedFrameSizeFromFp / kPointerSize))
: 0;
// Discard only slots that won't be used by new parameters.
sp_slot_delta += stack_param_delta;
return sp_slot_delta;
}
OutOfLineCode::OutOfLineCode(CodeGenerator* gen)
: frame_(gen->frame()), masm_(gen->masm()), next_(gen->ools_) {
gen->ools_ = this;
......
......@@ -96,6 +96,9 @@ class CodeGenerator final : public GapResolver::Assembler {
// Generates code to deconstruct a the caller's frame, including arguments.
void AssembleDeconstructActivationRecord(int stack_param_delta);
// Generates code to manipulate the stack in preparation for a tail call.
void AssemblePrepareTailCall(int stack_param_delta);
// ===========================================================================
// ============== Architecture-specific gap resolver methods. ================
// ===========================================================================
......@@ -140,6 +143,12 @@ class CodeGenerator final : public GapResolver::Assembler {
void EnsureSpaceForLazyDeopt();
void MarkLazyDeoptSite();
// Converts the delta in the number of stack parameter passed from a tail
// caller to the callee into the distance (in pointers) the SP must be
// adjusted, taking frame elision and other relevant factors into
// consideration.
int TailCallFrameStackSlotDelta(int stack_param_delta);
// ===========================================================================
struct DeoptimizationState : ZoneObject {
......
......@@ -327,18 +327,23 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ add(esp, Immediate(sp_slot_delta * kPointerSize));
}
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ mov(esp, ebp);
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
__ pop(ebp);
}
if (stack_param_delta < 0) {
int offset = -(stack_param_delta + 1) * kPointerSize;
__ pop(Operand(esp, offset));
if (offset != 0) {
__ add(esp, Immediate(offset));
}
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ sub(esp, Immediate(-sp_slot_delta * kPointerSize));
}
}
......@@ -408,6 +413,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ PrepareCallCFunction(num_parameters, i.TempRegister(0));
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
......
......@@ -46,6 +46,7 @@ enum class RecordWriteMode { kValueIsMap, kValueIsPointer, kValueIsAny };
V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchPrepareTailCall) \
V(ArchLazyBailout) \
V(ArchJmp) \
V(ArchLookupSwitch) \
......
......@@ -128,9 +128,15 @@ class OperandGenerator {
reg.code(), GetVReg(node)));
}
InstructionOperand UseExplicit(Register reg) {
InstructionOperand UseExplicit(LinkageLocation location) {
MachineType machine_type = InstructionSequence::DefaultRepresentation();
return ExplicitOperand(LocationOperand::REGISTER, machine_type, reg.code());
if (location.IsRegister()) {
return ExplicitOperand(LocationOperand::REGISTER, machine_type,
location.AsRegister());
} else {
return ExplicitOperand(LocationOperand::STACK_SLOT, machine_type,
location.GetLocation());
}
}
InstructionOperand UseImmediate(Node* node) {
......@@ -142,6 +148,18 @@ class OperandGenerator {
return Use(node, ToUnallocatedOperand(location, type, GetVReg(node)));
}
// Used to force gap moves from the from_location to the to_location
// immediately before an instruction.
InstructionOperand UsePointerLocation(LinkageLocation to_location,
LinkageLocation from_location) {
MachineType type = static_cast<MachineType>(kTypeAny | kMachPtr);
UnallocatedOperand casted_from_operand =
UnallocatedOperand::cast(TempLocation(from_location, type));
selector_->Emit(kArchNop, casted_from_operand);
return ToUnallocatedOperand(to_location, type,
casted_from_operand.virtual_register());
}
InstructionOperand TempRegister() {
return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
UnallocatedOperand::USED_AT_START,
......
......@@ -507,6 +507,40 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
}
DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count -
buffer->frame_state_value_count());
if (call_tail && stack_param_delta != 0) {
// For tail calls that change the size of their parameter list, move the
// saved caller return address, parent frame pointer and parent constant
// pool pointer to just above the parameters.
// Return address
LinkageLocation saved_return_location =
LinkageLocation::ForSavedCallerReturnAddress();
InstructionOperand return_address =
g.UsePointerLocation(LinkageLocation::ConvertToTailCallerLocation(
saved_return_location, stack_param_delta),
saved_return_location);
buffer->instruction_args.push_back(return_address);
// Parent frame pointer
LinkageLocation saved_frame_location =
LinkageLocation::ForSavedCallerFramePtr();
InstructionOperand saved_frame =
g.UsePointerLocation(LinkageLocation::ConvertToTailCallerLocation(
saved_frame_location, stack_param_delta),
saved_frame_location);
buffer->instruction_args.push_back(saved_frame);
if (V8_EMBEDDED_CONSTANT_POOL) {
// Constant pool pointer
LinkageLocation saved_cp_location =
LinkageLocation::ForSavedCallerConstantPool();
InstructionOperand saved_cp =
g.UsePointerLocation(LinkageLocation::ConvertToTailCallerLocation(
saved_cp_location, stack_param_delta),
saved_cp_location);
buffer->instruction_args.push_back(saved_cp);
}
}
}
......@@ -1294,6 +1328,9 @@ void InstructionSelector::VisitTailCall(Node* node) {
buffer.instruction_args.push_back(g.TempImmediate(stack_param_delta));
Emit(kArchPrepareTailCall, g.NoOutput(),
g.TempImmediate(stack_param_delta));
// Emit the tailcall instruction.
Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
&buffer.instruction_args.front());
......
......@@ -91,10 +91,7 @@ bool CallDescriptor::HasSameReturnLocationsAs(
bool CallDescriptor::CanTailCall(const Node* node,
int* stack_param_delta) const {
// TODO(danno): TF only current supports tail calls where the number of stack
// parameters of the callee is the same or fewer of the caller.
CallDescriptor const* other = OpParameter<CallDescriptor const*>(node);
if (!HasSameReturnLocationsAs(other)) return false;
size_t current_input = 0;
size_t other_input = 0;
*stack_param_delta = 0;
......@@ -103,14 +100,14 @@ bool CallDescriptor::CanTailCall(const Node* node,
while (more_other || more_this) {
if (other_input < other->InputCount()) {
if (!other->GetInputLocation(other_input).IsRegister()) {
(*stack_param_delta)++;
(*stack_param_delta)--;
}
} else {
more_other = false;
}
if (current_input < InputCount()) {
if (!GetInputLocation(current_input).IsRegister()) {
(*stack_param_delta)--;
(*stack_param_delta)++;
}
} else {
more_this = false;
......@@ -118,7 +115,7 @@ bool CallDescriptor::CanTailCall(const Node* node,
++current_input;
++other_input;
}
return *stack_param_delta <= 0;
return HasSameReturnLocationsAs(OpParameter<CallDescriptor const*>(node));
}
......
......@@ -57,11 +57,30 @@ class LinkageLocation {
return LinkageLocation(STACK_SLOT, slot);
}
static LinkageLocation ForSavedCallerReturnAddress() {
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kCallerPCOffset) /
kPointerSize);
}
static LinkageLocation ForSavedCallerFramePtr() {
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kCallerFPOffset) /
kPointerSize);
}
static LinkageLocation ForSavedCallerConstantPool() {
DCHECK(V8_EMBEDDED_CONSTANT_POOL);
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kConstantPoolOffset) /
kPointerSize);
}
static LinkageLocation ConvertToTailCallerLocation(
LinkageLocation caller_location, int stack_param_delta) {
if (!caller_location.IsRegister()) {
return LinkageLocation(STACK_SLOT,
caller_location.GetLocation() + stack_param_delta);
caller_location.GetLocation() - stack_param_delta);
}
return caller_location;
}
......
......@@ -454,20 +454,23 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
int stack_pointer_delta = 0;
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ mov(sp, fp);
__ lw(fp, MemOperand(sp, 0 * kPointerSize));
__ lw(ra, MemOperand(sp, 1 * kPointerSize));
stack_pointer_delta = 2 * kPointerSize;
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ addiu(sp, sp, sp_slot_delta * kPointerSize);
}
if (stack_param_delta < 0) {
stack_pointer_delta += -stack_param_delta * kPointerSize;
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
__ Pop(ra, fp);
}
if (stack_pointer_delta != 0) {
__ addiu(sp, sp, stack_pointer_delta);
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ Subu(sp, sp, Operand(-sp_slot_delta * kPointerSize));
}
}
......@@ -540,6 +543,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
......
......@@ -454,20 +454,23 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
int stack_pointer_delta = 0;
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ mov(sp, fp);
__ ld(fp, MemOperand(sp, 0 * kPointerSize));
__ ld(ra, MemOperand(sp, 1 * kPointerSize));
stack_pointer_delta = 2 * kPointerSize;
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ daddiu(sp, sp, sp_slot_delta * kPointerSize);
}
if (stack_param_delta < 0) {
stack_pointer_delta += -stack_param_delta * kPointerSize;
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
__ Pop(ra, fp);
}
if (stack_pointer_delta != 0) {
__ daddiu(sp, sp, stack_pointer_delta);
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ Dsubu(sp, sp, Operand(-sp_slot_delta * kPointerSize));
}
}
......@@ -538,6 +541,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
......
......@@ -574,18 +574,23 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta > 0) {
__ addq(rsp, Immediate(sp_slot_delta * kPointerSize));
}
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ movq(rsp, rbp);
int spill_slots = frame()->GetSpillSlotCount();
bool has_frame = descriptor->IsJSFunctionCall() || spill_slots > 0;
if (has_frame) {
__ popq(rbp);
}
if (stack_param_delta < 0) {
int offset = -(stack_param_delta + 1) * kPointerSize;
__ popq(Operand(rsp, offset));
if (offset != 0) {
__ addq(rsp, Immediate(offset));
}
}
void CodeGenerator::AssemblePrepareTailCall(int stack_param_delta) {
int sp_slot_delta = TailCallFrameStackSlotDelta(stack_param_delta);
if (sp_slot_delta < 0) {
__ subq(rsp, Immediate(-sp_slot_delta * kPointerSize));
}
}
......@@ -655,6 +660,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ PrepareCallCFunction(num_parameters);
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall(i.InputInt32(instr->InputCount() - 1));
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
......
......@@ -157,8 +157,8 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCallee) {
const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
int stack_param_delta = 0;
EXPECT_FALSE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(1, stack_param_delta);
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
}
......@@ -179,7 +179,7 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCaller) {
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
EXPECT_EQ(1, stack_param_delta);
}
......@@ -314,7 +314,7 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCallerRegistersAndStack) {
Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
EXPECT_EQ(1, stack_param_delta);
}
......@@ -341,8 +341,8 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCalleeRegistersAndStack) {
Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
int stack_param_delta = 0;
EXPECT_FALSE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(1, stack_param_delta);
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
}
} // namespace compiler
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment