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) {
} while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
......@@ -420,7 +425,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(cp, kScratchReg);
__ Assert(eq, kWrongFunctionContext);
}
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ ldr(ip, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(ip);
DCHECK_EQ(LeaveCC, i.OutputSBit());
......
......@@ -448,13 +448,17 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
} while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ Mov(jssp, fp);
__ 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
......@@ -514,7 +519,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(cp, temp);
__ Assert(eq, kWrongFunctionContext);
}
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
__ Jump(x10);
break;
......
......@@ -94,7 +94,7 @@ class CodeGenerator final : public GapResolver::Assembler {
void AssembleReturn();
// Generates code to deconstruct a the caller's frame, including arguments.
void AssembleDeconstructActivationRecord();
void AssembleDeconstructActivationRecord(int stack_param_delta);
// ===========================================================================
// ============== Architecture-specific gap resolver methods. ================
......
......@@ -326,13 +326,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} while (false)
void CodeGenerator::AssembleDeconstructActivationRecord() {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ mov(esp, 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET);
......@@ -385,7 +393,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmp(esi, FieldOperand(func, JSFunction::kContextOffset));
__ Assert(equal, kWrongFunctionContext);
}
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break;
}
......
......@@ -374,8 +374,8 @@ struct CallBuffer {
// TODO(bmeurer): Get rid of the CallBuffer business and make
// InstructionSelector::VisitCall platform independent instead.
void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
bool call_code_immediate,
bool call_address_immediate) {
CallBufferFlags flags,
int stack_param_delta) {
OperandGenerator g(this);
DCHECK_LE(call->op()->ValueOutputCount(),
static_cast<int>(buffer->descriptor->ReturnCount()));
......@@ -426,6 +426,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// The first argument is always the callee code.
Node* callee = call->InputAt(0);
bool call_code_immediate = (flags & kCallCodeImmediate) != 0;
bool call_address_immediate = (flags & kCallAddressImmediate) != 0;
switch (buffer->descriptor->kind()) {
case CallDescriptor::kCallCodeObject:
buffer->instruction_args.push_back(
......@@ -478,14 +480,20 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// as an InstructionOperand argument to the call.
auto iter(call->inputs().begin());
size_t pushed_count = 0;
bool call_tail = (flags & kCallTail) != 0;
for (size_t index = 0; index < input_count; ++iter, ++index) {
DCHECK(iter != call->inputs().end());
DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState);
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 =
g.UseLocation(*iter, buffer->descriptor->GetInputLocation(index),
buffer->descriptor->GetInputType(index));
if (UnallocatedOperand::cast(op).HasFixedSlotPolicy()) {
g.UseLocation(*iter, location, buffer->descriptor->GetInputType(index));
if (UnallocatedOperand::cast(op).HasFixedSlotPolicy() && !call_tail) {
int stack_index = -UnallocatedOperand::cast(op).fixed_slot_index() - 1;
if (static_cast<size_t>(stack_index) >= buffer->pushed_nodes.size()) {
buffer->pushed_nodes.resize(stack_index + 1, NULL);
......@@ -1173,7 +1181,8 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// the code object in a register if there are multiple uses of it.
// Improve constant pool and the heuristics in the register allocator
// 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);
......@@ -1226,11 +1235,17 @@ void InstructionSelector::VisitTailCall(Node* node) {
// 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);
// 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.
InstructionCode opcode;
......@@ -1247,6 +1262,8 @@ void InstructionSelector::VisitTailCall(Node* node) {
}
opcode |= MiscField::encode(descriptor->flags());
buffer.instruction_args.push_back(g.TempImmediate(stack_param_delta));
// Emit the tailcall instruction.
Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
&buffer.instruction_args.front());
......@@ -1260,7 +1277,11 @@ void InstructionSelector::VisitTailCall(Node* node) {
CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
// 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);
......
......@@ -164,14 +164,20 @@ class InstructionSelector final {
// operand {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,
// corresponding
// to the inputs and outputs of the call.
// {call_code_immediate} to generate immediate operands to calls of code.
// {call_address_immediate} to generate immediate operands to address calls.
void InitializeCallBuffer(Node* call, CallBuffer* buffer,
bool call_code_immediate,
bool call_address_immediate);
CallBufferFlags flags, int stack_param_delta = 0);
bool IsTailCallAddressImmediate();
FrameStateDescriptor* GetFrameStateDescriptor(Node* node);
......
......@@ -114,6 +114,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
return ReduceThrowNotDateError(node);
case Runtime::kInlineCall:
return ReduceCall(node);
case Runtime::kInlineTailCall:
return ReduceTailCall(node);
default:
break;
}
......@@ -648,6 +650,16 @@ Reduction JSIntrinsicLowering::ReduceToString(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();
NodeProperties::ChangeOp(
node, javascript()->CallFunction(arity, STRICT, VectorSlotPair(),
......
......@@ -70,6 +70,7 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Reduction ReduceToPrimitive(Node* node);
Reduction ReduceToString(Node* node);
Reduction ReduceCall(Node* node);
Reduction ReduceTailCall(Node* node);
Reduction Change(Node* node, const Operator* op);
Reduction Change(Node* node, const Operator* op, Node* a, Node* b);
......
......@@ -89,79 +89,36 @@ bool CallDescriptor::HasSameReturnLocationsAs(
}
bool CallDescriptor::CanTailCall(const Node* node) const {
// Determine the number of stack parameters passed in
size_t stack_params = 0;
for (size_t i = 0; i < InputCount(); ++i) {
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.
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;
while (true) {
if (other_input >= other->InputCount()) {
while (current_input < InputCount()) {
if (!GetInputLocation(current_input).IsRegister()) {
return false;
}
++current_input;
*stack_param_delta = 0;
bool more_other = true;
bool more_this = true;
while (more_other || more_this) {
if (other_input < other->InputCount()) {
if (!other->GetInputLocation(other_input).IsRegister()) {
(*stack_param_delta)++;
}
return true;
} else {
more_other = false;
}
if (current_input >= InputCount()) {
while (other_input < other->InputCount()) {
if (!other->GetInputLocation(other_input).IsRegister()) {
return false;
}
++other_input;
if (current_input < InputCount()) {
if (!GetInputLocation(current_input).IsRegister()) {
(*stack_param_delta)--;
}
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 (input->opcode() != IrOpcode::kParameter) {
return false;
}
// Make sure that the parameter input passed through to the tail call
// corresponds to the correct stack slot.
size_t param_index = ParameterIndexOf(input->op());
if (param_index != current_input - 1) {
return false;
} else {
more_this = false;
}
++current_input;
++other_input;
}
UNREACHABLE();
return false;
return *stack_param_delta <= 0;
}
......@@ -258,6 +215,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kInlineToString:
return 1;
case Runtime::kInlineCall:
case Runtime::kInlineTailCall:
case Runtime::kInlineDeoptimizeNow:
case Runtime::kInlineThrowNotDateError:
return 2;
......
......@@ -57,6 +57,15 @@ class LinkageLocation {
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:
friend class CallDescriptor;
friend class OperandGenerator;
......@@ -222,7 +231,7 @@ class CallDescriptor final : public ZoneObject {
bool HasSameReturnLocationsAs(const CallDescriptor* other) const;
bool CanTailCall(const Node* call) const;
bool CanTailCall(const Node* call, int* stack_param_delta) const;
private:
friend class Linkage;
......
......@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
} while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() {
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) {
__ 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
......@@ -506,7 +517,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ 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));
__ Jump(at);
break;
......
......@@ -446,11 +446,21 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
} while (0)
void CodeGenerator::AssembleDeconstructActivationRecord() {
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) {
__ 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(Handle<Code>::cast(i.InputHeapObject(0)),
RelocInfo::CODE_TARGET);
......@@ -504,7 +515,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ ld(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset));
__ 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));
__ Jump(at);
break;
......
......@@ -573,13 +573,20 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} while (false)
void CodeGenerator::AssembleDeconstructActivationRecord() {
void CodeGenerator::AssembleDeconstructActivationRecord(int stack_param_delta) {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
int stack_slots = frame()->GetSpillSlotCount();
if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
__ movq(rsp, 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) {
break;
}
case kArchTailCallCodeObject: {
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
if (HasImmediateInput(instr, 0)) {
Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
__ jmp(code, RelocInfo::CODE_TARGET);
......@@ -632,7 +640,8 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset));
__ Assert(equal, kWrongFunctionContext);
}
AssembleDeconstructActivationRecord();
int stack_param_delta = i.InputInt32(instr->InputCount() - 1);
AssembleDeconstructActivationRecord(stack_param_delta);
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break;
}
......
......@@ -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) {
HandleScope scope(isolate);
DCHECK(args.length() == 5);
......
......@@ -258,6 +258,7 @@ namespace internal {
F(BoundFunctionGetBindings, 1, 1) \
F(NewObjectFromBound, 1, 1) \
F(Call, -1 /* >= 2 */, 1) \
F(TailCall, -1 /* >= 2 */, 1) \
F(Apply, 5, 1) \
F(GetNewTarget, 0, 1) \
F(ConvertReceiver, 1, 1) \
......
......@@ -1028,6 +1028,7 @@
'string-replace-with-empty': [SKIP],
'string-slices': [SKIP],
'switch-opt': [SKIP],
'tail-call-intrinsic': [SKIP],
'tools/profile': [SKIP],
'tools/profviz': [SKIP],
'try-finally-continue': [SKIP],
......
......@@ -16,7 +16,7 @@ tailee1 = function() {
if (count1-- == 0) {
return this;
}
return %_Call(tailee1, this);
return %_TailCall(tailee1, this);
};
%OptimizeFunctionOnNextCall(tailee1);
......@@ -33,25 +33,25 @@ tailee2 = function(px) {
if ((count2 | 0) === 0) {
return this;
}
return %_Call(tailee2, this, px);
return %_TailCall(tailee2, this, px);
};
%OptimizeFunctionOnNextCall(tailee2);
assertEquals(p1, tailee2.call(p1, p2));
// Ensure swapped 2 parameters don't trigger a tail call (parameter swizzling
// for the tail call isn't supported yet).
var count3 = 100000;
// Ensure swapped 2 parameters trigger a tail call and do the appropriate
// parameters swapping
var count3 = 999999;
tailee3 = function(px) {
"use strict";
if (count3-- == 0) {
return this;
}
return %_Call(tailee3, px, this);
return %_TailCall(tailee3, px, this);
};
%OptimizeFunctionOnNextCall(tailee3);
assertThrows(function() { tailee3.call(p1, p2); });
assertEquals(p2, tailee3.call(p1, p2));
// Ensure too many parameters defeats the tail call optimization (currently
// unsupported).
......@@ -61,22 +61,48 @@ tailee4 = function(px) {
if (count4-- == 0) {
return this;
}
return %_Call(tailee4, this, px, undefined);
return %_TailCall(tailee4, this, px, undefined);
};
%OptimizeFunctionOnNextCall(tailee4);
assertThrows(function() { tailee4.call(p1, p2); });
// Ensure too few parameters defeats the tail call optimization (currently
// unsupported).
// Ensure that calling the arguments adapter defeats the tail call optimization.
var count5 = 1000000;
tailee5 = function(px) {
"use strict";
if (count5-- == 0) {
return this;
}
return %_Call(tailee5, this);
return %_TailCall(tailee5, this);
};
%OptimizeFunctionOnNextCall(tailee5);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
CommonOperatorBuilder common(zone());
const Operator* op = common.Call(desc2);
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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) {
const Operator* op = common.Call(desc2);
Node* const node =
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
......
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