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

[turbofan] Better and more sane support for tail calls

* Limit triggering of tail calls to explicit use of a new inline runtime
  function %_TailCall. %_TailCall works just like %_Call except for using
  tail-calling mechanics (currently only in TF).
* Remove hack that recognized some specific usages of %_Call and converted them
  into tail calls.
* Support tail calls for all calls where the number of callee stack parameters
  is less than or equal to the number of caller stack parameters.
* Use the gap resolver to swizzle parameters and registers to tail calls.

BUG=v8:4076
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#31987}
parent c42f188c
...@@ -354,12 +354,16 @@ Condition FlagsConditionToCondition(FlagsCondition condition) { ...@@ -354,12 +354,16 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
} while (0) } while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ LeaveFrame(StackFrame::MANUAL); __ LeaveFrame(StackFrame::MANUAL);
} }
if (stack_param_delta < 0) {
int offset = -stack_param_delta * kPointerSize;
__ add(sp, sp, Operand(offset));
}
} }
...@@ -385,7 +389,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -385,7 +389,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) { if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)), __ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
...@@ -420,7 +425,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -420,7 +425,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(cp, kScratchReg); __ cmp(cp, kScratchReg);
__ Assert(eq, kWrongFunctionContext); __ Assert(eq, kWrongFunctionContext);
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ ldr(ip, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); __ ldr(ip, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(ip); __ Jump(ip);
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
......
...@@ -448,13 +448,17 @@ Condition FlagsConditionToCondition(FlagsCondition condition) { ...@@ -448,13 +448,17 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
} while (0) } while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ Mov(jssp, fp); __ Mov(jssp, fp);
__ Pop(fp, lr); __ Pop(fp, lr);
} }
if (stack_param_delta < 0) {
int offset = -stack_param_delta * kPointerSize;
__ Add(jssp, jssp, Operand(offset));
}
} }
...@@ -477,7 +481,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -477,7 +481,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) { if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)), __ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
...@@ -514,7 +519,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -514,7 +519,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(cp, temp); __ cmp(cp, temp);
__ Assert(eq, kWrongFunctionContext); __ Assert(eq, kWrongFunctionContext);
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); __ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(x10); __ Jump(x10);
break; break;
......
...@@ -94,7 +94,7 @@ class CodeGenerator final : public GapResolver::Assembler { ...@@ -94,7 +94,7 @@ class CodeGenerator final : public GapResolver::Assembler {
void AssembleReturn(); void AssembleReturn();
// Generates code to deconstruct a the caller's frame, including arguments. // Generates code to deconstruct a the caller's frame, including arguments.
void AssembleDeconstructActivationRecord(); void AssembleDeconstructActivationRecord(int stack_param_delta);
// =========================================================================== // ===========================================================================
// ============== Architecture-specific gap resolver methods. ================ // ============== Architecture-specific gap resolver methods. ================
......
...@@ -326,13 +326,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -326,13 +326,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} while (false) } while (false)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ mov(esp, ebp); __ mov(esp, ebp);
__ pop(ebp); __ 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));
}
}
} }
...@@ -355,7 +362,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -355,7 +362,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (HasImmediateInput(instr, 0)) { if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0)); Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET); __ jmp(code, RelocInfo::CODE_TARGET);
...@@ -385,7 +393,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -385,7 +393,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset));
__ Assert(equal, kWrongFunctionContext); __ Assert(equal, kWrongFunctionContext);
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break; break;
} }
......
...@@ -374,8 +374,8 @@ struct CallBuffer { ...@@ -374,8 +374,8 @@ struct CallBuffer {
// TODO(bmeurer): Get rid of the CallBuffer business and make // TODO(bmeurer): Get rid of the CallBuffer business and make
// InstructionSelector::VisitCall platform independent instead. // InstructionSelector::VisitCall platform independent instead.
void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
bool call_code_immediate, CallBufferFlags flags,
bool call_address_immediate) { int stack_param_delta) {
OperandGenerator g(this); OperandGenerator g(this);
DCHECK_LE(call->op()->ValueOutputCount(), DCHECK_LE(call->op()->ValueOutputCount(),
static_cast<int>(buffer->descriptor->ReturnCount())); static_cast<int>(buffer->descriptor->ReturnCount()));
...@@ -426,6 +426,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -426,6 +426,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// The first argument is always the callee code. // The first argument is always the callee code.
Node* callee = call->InputAt(0); Node* callee = call->InputAt(0);
bool call_code_immediate = (flags & kCallCodeImmediate) != 0;
bool call_address_immediate = (flags & kCallAddressImmediate) != 0;
switch (buffer->descriptor->kind()) { switch (buffer->descriptor->kind()) {
case CallDescriptor::kCallCodeObject: case CallDescriptor::kCallCodeObject:
buffer->instruction_args.push_back( buffer->instruction_args.push_back(
...@@ -478,14 +480,20 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -478,14 +480,20 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// as an InstructionOperand argument to the call. // as an InstructionOperand argument to the call.
auto iter(call->inputs().begin()); auto iter(call->inputs().begin());
size_t pushed_count = 0; size_t pushed_count = 0;
bool call_tail = (flags & kCallTail) != 0;
for (size_t index = 0; index < input_count; ++iter, ++index) { for (size_t index = 0; index < input_count; ++iter, ++index) {
DCHECK(iter != call->inputs().end()); DCHECK(iter != call->inputs().end());
DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState); DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState);
if (index == 0) continue; // The first argument (callee) is already done. if (index == 0) continue; // The first argument (callee) is already done.
LinkageLocation location = buffer->descriptor->GetInputLocation(index);
if (call_tail) {
location = LinkageLocation::ConvertToTailCallerLocation(
location, stack_param_delta);
}
InstructionOperand op = InstructionOperand op =
g.UseLocation(*iter, buffer->descriptor->GetInputLocation(index), g.UseLocation(*iter, location, buffer->descriptor->GetInputType(index));
buffer->descriptor->GetInputType(index)); if (UnallocatedOperand::cast(op).HasFixedSlotPolicy() && !call_tail) {
if (UnallocatedOperand::cast(op).HasFixedSlotPolicy()) {
int stack_index = -UnallocatedOperand::cast(op).fixed_slot_index() - 1; int stack_index = -UnallocatedOperand::cast(op).fixed_slot_index() - 1;
if (static_cast<size_t>(stack_index) >= buffer->pushed_nodes.size()) { if (static_cast<size_t>(stack_index) >= buffer->pushed_nodes.size()) {
buffer->pushed_nodes.resize(stack_index + 1, NULL); buffer->pushed_nodes.resize(stack_index + 1, NULL);
...@@ -1173,7 +1181,8 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1173,7 +1181,8 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// the code object in a register if there are multiple uses of it. // the code object in a register if there are multiple uses of it.
// Improve constant pool and the heuristics in the register allocator // Improve constant pool and the heuristics in the register allocator
// for where to emit constants. // for where to emit constants.
InitializeCallBuffer(node, &buffer, true, true); CallBufferFlags call_buffer_flags(kCallCodeImmediate | kCallAddressImmediate);
InitializeCallBuffer(node, &buffer, call_buffer_flags);
EmitPrepareArguments(&(buffer.pushed_nodes), descriptor, node); EmitPrepareArguments(&(buffer.pushed_nodes), descriptor, node);
...@@ -1226,11 +1235,17 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -1226,11 +1235,17 @@ void InstructionSelector::VisitTailCall(Node* node) {
// TODO(turbofan): Relax restriction for stack parameters. // TODO(turbofan): Relax restriction for stack parameters.
if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) { int stack_param_delta = 0;
if (linkage()->GetIncomingDescriptor()->CanTailCall(node,
&stack_param_delta)) {
CallBuffer buffer(zone(), descriptor, nullptr); CallBuffer buffer(zone(), descriptor, nullptr);
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, IsTailCallAddressImmediate()); CallBufferFlags flags(kCallCodeImmediate | kCallTail);
if (IsTailCallAddressImmediate()) {
flags |= kCallAddressImmediate;
}
InitializeCallBuffer(node, &buffer, flags, stack_param_delta);
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
...@@ -1247,6 +1262,8 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -1247,6 +1262,8 @@ void InstructionSelector::VisitTailCall(Node* node) {
} }
opcode |= MiscField::encode(descriptor->flags()); opcode |= MiscField::encode(descriptor->flags());
buffer.instruction_args.push_back(g.TempImmediate(stack_param_delta));
// Emit the tailcall instruction. // Emit the tailcall instruction.
Emit(opcode, 0, nullptr, buffer.instruction_args.size(), Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
&buffer.instruction_args.front()); &buffer.instruction_args.front());
...@@ -1260,7 +1277,11 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -1260,7 +1277,11 @@ void InstructionSelector::VisitTailCall(Node* node) {
CallBuffer buffer(zone(), descriptor, frame_state_descriptor); CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, IsTailCallAddressImmediate()); CallBufferFlags flags = kCallCodeImmediate;
if (IsTailCallAddressImmediate()) {
flags |= kCallAddressImmediate;
}
InitializeCallBuffer(node, &buffer, flags);
EmitPrepareArguments(&(buffer.pushed_nodes), descriptor, node); EmitPrepareArguments(&(buffer.pushed_nodes), descriptor, node);
......
...@@ -164,14 +164,20 @@ class InstructionSelector final { ...@@ -164,14 +164,20 @@ class InstructionSelector final {
// operand {op}. // operand {op}.
void MarkAsRepresentation(MachineType rep, const InstructionOperand& op); void MarkAsRepresentation(MachineType rep, const InstructionOperand& op);
enum CallBufferFlag {
kCallCodeImmediate = 1u << 0,
kCallAddressImmediate = 1u << 1,
kCallTail = 1u << 2
};
typedef base::Flags<CallBufferFlag> CallBufferFlags;
// Initialize the call buffer with the InstructionOperands, nodes, etc, // Initialize the call buffer with the InstructionOperands, nodes, etc,
// corresponding // corresponding
// to the inputs and outputs of the call. // to the inputs and outputs of the call.
// {call_code_immediate} to generate immediate operands to calls of code. // {call_code_immediate} to generate immediate operands to calls of code.
// {call_address_immediate} to generate immediate operands to address calls. // {call_address_immediate} to generate immediate operands to address calls.
void InitializeCallBuffer(Node* call, CallBuffer* buffer, void InitializeCallBuffer(Node* call, CallBuffer* buffer,
bool call_code_immediate, CallBufferFlags flags, int stack_param_delta = 0);
bool call_address_immediate);
bool IsTailCallAddressImmediate(); bool IsTailCallAddressImmediate();
FrameStateDescriptor* GetFrameStateDescriptor(Node* node); FrameStateDescriptor* GetFrameStateDescriptor(Node* node);
......
...@@ -114,6 +114,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) { ...@@ -114,6 +114,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
return ReduceThrowNotDateError(node); return ReduceThrowNotDateError(node);
case Runtime::kInlineCall: case Runtime::kInlineCall:
return ReduceCall(node); return ReduceCall(node);
case Runtime::kInlineTailCall:
return ReduceTailCall(node);
default: default:
break; break;
} }
...@@ -648,6 +650,16 @@ Reduction JSIntrinsicLowering::ReduceToString(Node* node) { ...@@ -648,6 +650,16 @@ Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
Reduction JSIntrinsicLowering::ReduceCall(Node* node) { Reduction JSIntrinsicLowering::ReduceCall(Node* node) {
size_t const arity = CallRuntimeParametersOf(node->op()).arity();
NodeProperties::ChangeOp(
node, javascript()->CallFunction(arity, STRICT, VectorSlotPair(),
ConvertReceiverMode::kAny,
TailCallMode::kDisallow));
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceTailCall(Node* node) {
size_t const arity = CallRuntimeParametersOf(node->op()).arity(); size_t const arity = CallRuntimeParametersOf(node->op()).arity();
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->CallFunction(arity, STRICT, VectorSlotPair(), node, javascript()->CallFunction(arity, STRICT, VectorSlotPair(),
......
...@@ -70,6 +70,7 @@ class JSIntrinsicLowering final : public AdvancedReducer { ...@@ -70,6 +70,7 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Reduction ReduceToPrimitive(Node* node); Reduction ReduceToPrimitive(Node* node);
Reduction ReduceToString(Node* node); Reduction ReduceToString(Node* node);
Reduction ReduceCall(Node* node); Reduction ReduceCall(Node* node);
Reduction ReduceTailCall(Node* node);
Reduction Change(Node* node, const Operator* op); Reduction Change(Node* node, const Operator* op);
Reduction Change(Node* node, const Operator* op, Node* a, Node* b); Reduction Change(Node* node, const Operator* op, Node* a, Node* b);
......
...@@ -89,79 +89,36 @@ bool CallDescriptor::HasSameReturnLocationsAs( ...@@ -89,79 +89,36 @@ bool CallDescriptor::HasSameReturnLocationsAs(
} }
bool CallDescriptor::CanTailCall(const Node* node) const { bool CallDescriptor::CanTailCall(const Node* node,
// Determine the number of stack parameters passed in int* stack_param_delta) const {
size_t stack_params = 0; // TODO(danno): TF only current supports tail calls where the number of stack
for (size_t i = 0; i < InputCount(); ++i) { // parameters of the callee is the same or fewer of the caller.
if (!GetInputLocation(i).IsRegister()) {
++stack_params;
}
}
// Ensure the input linkage contains the stack parameters in the right order
size_t current_stack_param = 0;
for (size_t i = 0; i < InputCount(); ++i) {
if (!GetInputLocation(i).IsRegister()) {
if (GetInputLocation(i) != LinkageLocation::ForCallerFrameSlot(
static_cast<int>(current_stack_param) -
static_cast<int>(stack_params))) {
return false;
}
++current_stack_param;
}
}
// Tail calling is currently allowed if return locations match and all
// parameters are either in registers or on the stack but match exactly in
// number and content.
CallDescriptor const* other = OpParameter<CallDescriptor const*>(node); CallDescriptor const* other = OpParameter<CallDescriptor const*>(node);
if (!HasSameReturnLocationsAs(other)) return false; if (!HasSameReturnLocationsAs(other)) return false;
size_t current_input = 0; size_t current_input = 0;
size_t other_input = 0; size_t other_input = 0;
while (true) { *stack_param_delta = 0;
if (other_input >= other->InputCount()) { bool more_other = true;
while (current_input < InputCount()) { bool more_this = true;
if (!GetInputLocation(current_input).IsRegister()) { while (more_other || more_this) {
return false; if (other_input < other->InputCount()) {
}
++current_input;
}
return true;
}
if (current_input >= InputCount()) {
while (other_input < other->InputCount()) {
if (!other->GetInputLocation(other_input).IsRegister()) { if (!other->GetInputLocation(other_input).IsRegister()) {
return false; (*stack_param_delta)++;
} }
++other_input; } else {
} more_other = false;
return true;
}
if (GetInputLocation(current_input).IsRegister()) {
++current_input;
continue;
}
if (other->GetInputLocation(other_input).IsRegister()) {
++other_input;
continue;
}
if (GetInputLocation(current_input) !=
other->GetInputLocation(other_input)) {
return false;
} }
Node* input = node->InputAt(static_cast<int>(other_input)); if (current_input < InputCount()) {
if (input->opcode() != IrOpcode::kParameter) { if (!GetInputLocation(current_input).IsRegister()) {
return false; (*stack_param_delta)--;
} }
// Make sure that the parameter input passed through to the tail call } else {
// corresponds to the correct stack slot. more_this = false;
size_t param_index = ParameterIndexOf(input->op());
if (param_index != current_input - 1) {
return false;
} }
++current_input; ++current_input;
++other_input; ++other_input;
} }
UNREACHABLE(); return *stack_param_delta <= 0;
return false;
} }
...@@ -258,6 +215,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) { ...@@ -258,6 +215,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kInlineToString: case Runtime::kInlineToString:
return 1; return 1;
case Runtime::kInlineCall: case Runtime::kInlineCall:
case Runtime::kInlineTailCall:
case Runtime::kInlineDeoptimizeNow: case Runtime::kInlineDeoptimizeNow:
case Runtime::kInlineThrowNotDateError: case Runtime::kInlineThrowNotDateError:
return 2; return 2;
......
...@@ -57,6 +57,15 @@ class LinkageLocation { ...@@ -57,6 +57,15 @@ class LinkageLocation {
return LinkageLocation(STACK_SLOT, slot); return LinkageLocation(STACK_SLOT, slot);
} }
static LinkageLocation ConvertToTailCallerLocation(
LinkageLocation caller_location, int stack_param_delta) {
if (!caller_location.IsRegister()) {
return LinkageLocation(STACK_SLOT,
caller_location.GetLocation() + stack_param_delta);
}
return caller_location;
}
private: private:
friend class CallDescriptor; friend class CallDescriptor;
friend class OperandGenerator; friend class OperandGenerator;
...@@ -222,7 +231,7 @@ class CallDescriptor final : public ZoneObject { ...@@ -222,7 +231,7 @@ class CallDescriptor final : public ZoneObject {
bool HasSameReturnLocationsAs(const CallDescriptor* other) const; bool HasSameReturnLocationsAs(const CallDescriptor* other) const;
bool CanTailCall(const Node* call) const; bool CanTailCall(const Node* call, int* stack_param_delta) const;
private: private:
friend class Linkage; friend class Linkage;
......
...@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate, ...@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
} while (0) } while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
int stack_pointer_delta = 0;
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ LeaveFrame(StackFrame::MANUAL); __ mov(sp, fp);
__ lw(fp, MemOperand(sp, 0 * kPointerSize));
__ lw(ra, MemOperand(sp, 1 * kPointerSize));
stack_pointer_delta = 2 * kPointerSize;
}
if (stack_param_delta < 0) {
stack_pointer_delta += -stack_param_delta * kPointerSize;
}
if (stack_pointer_delta != 0) {
__ addiu(sp, sp, stack_pointer_delta);
} }
} }
...@@ -474,7 +484,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -474,7 +484,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) { if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)), __ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
...@@ -506,7 +517,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -506,7 +517,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Assert(eq, kWrongFunctionContext, cp, Operand(kScratchReg)); __ Assert(eq, kWrongFunctionContext, cp, Operand(kScratchReg));
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ lw(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); __ lw(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(at); __ Jump(at);
break; break;
......
...@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate, ...@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
} while (0) } while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
int stack_pointer_delta = 0;
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ LeaveFrame(StackFrame::MANUAL); __ mov(sp, fp);
__ ld(fp, MemOperand(sp, 0 * kPointerSize));
__ ld(ra, MemOperand(sp, 1 * kPointerSize));
stack_pointer_delta = 2 * kPointerSize;
}
if (stack_param_delta < 0) {
stack_pointer_delta += -stack_param_delta * kPointerSize;
}
if (stack_pointer_delta != 0) {
__ daddiu(sp, sp, stack_pointer_delta);
} }
} }
...@@ -474,7 +484,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -474,7 +484,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) { if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)), __ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
...@@ -504,7 +515,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -504,7 +515,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ ld(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset)); __ ld(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset));
__ Assert(eq, kWrongFunctionContext, cp, Operand(kScratchReg)); __ Assert(eq, kWrongFunctionContext, cp, Operand(kScratchReg));
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ ld(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); __ ld(at, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(at); __ Jump(at);
break; break;
......
...@@ -573,13 +573,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -573,13 +573,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} while (false) } while (false)
void CodeGenerator::AssembleDeconstructActivationRecord() { void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount(); int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) { if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ movq(rsp, rbp); __ movq(rsp, rbp);
__ popq(rbp); __ 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));
}
}
} }
...@@ -602,7 +609,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -602,7 +609,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
} }
case kArchTailCallCodeObject: { case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (HasImmediateInput(instr, 0)) { if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0)); Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET); __ jmp(code, RelocInfo::CODE_TARGET);
...@@ -632,7 +640,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -632,7 +640,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset)); __ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset));
__ Assert(equal, kWrongFunctionContext); __ Assert(equal, kWrongFunctionContext);
} }
AssembleDeconstructActivationRecord(); int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break; break;
} }
......
...@@ -528,6 +528,24 @@ RUNTIME_FUNCTION(Runtime_Call) { ...@@ -528,6 +528,24 @@ RUNTIME_FUNCTION(Runtime_Call) {
} }
RUNTIME_FUNCTION(Runtime_TailCall) {
HandleScope scope(isolate);
DCHECK_LE(2, args.length());
int const argc = args.length() - 2;
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, target, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 1);
ScopedVector<Handle<Object>> argv(argc);
for (int i = 0; i < argc; ++i) {
argv[i] = args.at<Object>(2 + i);
}
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, target, receiver, argc, argv.start()));
return *result;
}
RUNTIME_FUNCTION(Runtime_Apply) { RUNTIME_FUNCTION(Runtime_Apply) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 5); DCHECK(args.length() == 5);
......
...@@ -258,6 +258,7 @@ namespace internal { ...@@ -258,6 +258,7 @@ namespace internal {
F(BoundFunctionGetBindings, 1, 1) \ F(BoundFunctionGetBindings, 1, 1) \
F(NewObjectFromBound, 1, 1) \ F(NewObjectFromBound, 1, 1) \
F(Call, -1 /* >= 2 */, 1) \ F(Call, -1 /* >= 2 */, 1) \
F(TailCall, -1 /* >= 2 */, 1) \
F(Apply, 5, 1) \ F(Apply, 5, 1) \
F(GetNewTarget, 0, 1) \ F(GetNewTarget, 0, 1) \
F(ConvertReceiver, 1, 1) \ F(ConvertReceiver, 1, 1) \
......
...@@ -1028,6 +1028,7 @@ ...@@ -1028,6 +1028,7 @@
'string-replace-with-empty': [SKIP], 'string-replace-with-empty': [SKIP],
'string-slices': [SKIP], 'string-slices': [SKIP],
'switch-opt': [SKIP], 'switch-opt': [SKIP],
'tail-call-intrinsic': [SKIP],
'tools/profile': [SKIP], 'tools/profile': [SKIP],
'tools/profviz': [SKIP], 'tools/profviz': [SKIP],
'try-finally-continue': [SKIP], 'try-finally-continue': [SKIP],
......
...@@ -16,7 +16,7 @@ tailee1 = function() { ...@@ -16,7 +16,7 @@ tailee1 = function() {
if (count1-- == 0) { if (count1-- == 0) {
return this; return this;
} }
return %_Call(tailee1, this); return %_TailCall(tailee1, this);
}; };
%OptimizeFunctionOnNextCall(tailee1); %OptimizeFunctionOnNextCall(tailee1);
...@@ -33,25 +33,25 @@ tailee2 = function(px) { ...@@ -33,25 +33,25 @@ tailee2 = function(px) {
if ((count2 | 0) === 0) { if ((count2 | 0) === 0) {
return this; return this;
} }
return %_Call(tailee2, this, px); return %_TailCall(tailee2, this, px);
}; };
%OptimizeFunctionOnNextCall(tailee2); %OptimizeFunctionOnNextCall(tailee2);
assertEquals(p1, tailee2.call(p1, p2)); assertEquals(p1, tailee2.call(p1, p2));
// Ensure swapped 2 parameters don't trigger a tail call (parameter swizzling // Ensure swapped 2 parameters trigger a tail call and do the appropriate
// for the tail call isn't supported yet). // parameters swapping
var count3 = 100000; var count3 = 999999;
tailee3 = function(px) { tailee3 = function(px) {
"use strict"; "use strict";
if (count3-- == 0) { if (count3-- == 0) {
return this; return this;
} }
return %_Call(tailee3, px, this); return %_TailCall(tailee3, px, this);
}; };
%OptimizeFunctionOnNextCall(tailee3); %OptimizeFunctionOnNextCall(tailee3);
assertThrows(function() { tailee3.call(p1, p2); }); assertEquals(p2, tailee3.call(p1, p2));
// Ensure too many parameters defeats the tail call optimization (currently // Ensure too many parameters defeats the tail call optimization (currently
// unsupported). // unsupported).
...@@ -61,22 +61,48 @@ tailee4 = function(px) { ...@@ -61,22 +61,48 @@ tailee4 = function(px) {
if (count4-- == 0) { if (count4-- == 0) {
return this; return this;
} }
return %_Call(tailee4, this, px, undefined); return %_TailCall(tailee4, this, px, undefined);
}; };
%OptimizeFunctionOnNextCall(tailee4); %OptimizeFunctionOnNextCall(tailee4);
assertThrows(function() { tailee4.call(p1, p2); }); assertThrows(function() { tailee4.call(p1, p2); });
// Ensure too few parameters defeats the tail call optimization (currently // Ensure that calling the arguments adapter defeats the tail call optimization.
// unsupported).
var count5 = 1000000; var count5 = 1000000;
tailee5 = function(px) { tailee5 = function(px) {
"use strict"; "use strict";
if (count5-- == 0) { if (count5-- == 0) {
return this; return this;
} }
return %_Call(tailee5, this); return %_TailCall(tailee5, this);
}; };
%OptimizeFunctionOnNextCall(tailee5); %OptimizeFunctionOnNextCall(tailee5);
assertThrows(function() { tailee5.call(p1, p2); }); assertThrows(function() { tailee5.call(p1, p2); });
// Ensure tail calls with fewer stack parameters properly re-arranges the stack.
tailee6 = function(px) {
return px;
}
tailee7 = function(px, py, pz, pa, pb, pc) {
"use strict";
return %_TailCall(tailee6, this, pc);
};
%OptimizeFunctionOnNextCall(tailee6);
%OptimizeFunctionOnNextCall(tailee7);
assertEquals(110, tailee7.call(null, 15, 16, 17, 18, 0, 110));
tailee8 = function(px, py, pz, pa, pb) {
return pb + pz + px;
}
tailee9 = function(px, py, pz, pa, pb, pc) {
"use strict";
return %_TailCall(tailee8, this, pb, py, px, pa, pz);
};
%OptimizeFunctionOnNextCall(tailee8);
%OptimizeFunctionOnNextCall(tailee9);
assertEquals(32, tailee9.call(null, 15, 16, 17, 18, 0, 110));
...@@ -55,7 +55,9 @@ TEST_F(LinkageTailCall, EmptyToEmpty) { ...@@ -55,7 +55,9 @@ TEST_F(LinkageTailCall, EmptyToEmpty) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc); const Operator* op = common.Call(desc);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_TRUE(desc->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -71,7 +73,9 @@ TEST_F(LinkageTailCall, SameReturn) { ...@@ -71,7 +73,9 @@ TEST_F(LinkageTailCall, SameReturn) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -89,7 +93,9 @@ TEST_F(LinkageTailCall, DifferingReturn) { ...@@ -89,7 +93,9 @@ TEST_F(LinkageTailCall, DifferingReturn) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_FALSE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -108,7 +114,9 @@ TEST_F(LinkageTailCall, MoreRegisterParametersCallee) { ...@@ -108,7 +114,9 @@ TEST_F(LinkageTailCall, MoreRegisterParametersCallee) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -127,7 +135,9 @@ TEST_F(LinkageTailCall, MoreRegisterParametersCaller) { ...@@ -127,7 +135,9 @@ TEST_F(LinkageTailCall, MoreRegisterParametersCaller) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -146,7 +156,9 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCallee) { ...@@ -146,7 +156,9 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCallee) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_FALSE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(1, stack_param_delta);
} }
...@@ -165,7 +177,9 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCaller) { ...@@ -165,7 +177,9 @@ TEST_F(LinkageTailCall, MoreRegisterAndStackParametersCaller) {
CommonOperatorBuilder common(zone()); CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node::New(zone(), 1, op, 0, nullptr, false); Node* const node = Node::New(zone(), 1, op, 0, nullptr, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
} }
...@@ -189,7 +203,9 @@ TEST_F(LinkageTailCall, MatchingStackParameters) { ...@@ -189,7 +203,9 @@ TEST_F(LinkageTailCall, MatchingStackParameters) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -213,7 +229,9 @@ TEST_F(LinkageTailCall, NonMatchingStackParameters) { ...@@ -213,7 +229,9 @@ TEST_F(LinkageTailCall, NonMatchingStackParameters) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -238,7 +256,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCallerRegisters) { ...@@ -238,7 +256,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCallerRegisters) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -264,7 +284,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCalleeRegisters) { ...@@ -264,7 +284,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCalleeRegisters) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_TRUE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(0, stack_param_delta);
} }
...@@ -290,7 +312,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCallerRegistersAndStack) { ...@@ -290,7 +312,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCallerRegistersAndStack) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_TRUE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(-1, stack_param_delta);
} }
...@@ -316,7 +340,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCalleeRegistersAndStack) { ...@@ -316,7 +340,9 @@ TEST_F(LinkageTailCall, MatchingStackParametersExtraCalleeRegistersAndStack) {
const Operator* op = common.Call(desc2); const Operator* op = common.Call(desc2);
Node* const node = Node* const node =
Node::New(zone(), 1, op, arraysize(parameters), parameters, false); Node::New(zone(), 1, op, arraysize(parameters), parameters, false);
EXPECT_FALSE(desc1->CanTailCall(node)); int stack_param_delta = 0;
EXPECT_FALSE(desc1->CanTailCall(node, &stack_param_delta));
EXPECT_EQ(1, stack_param_delta);
} }
} // namespace compiler } // 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