[turbofan] Enable tail calls for %_CallRuntime.
authordanno <danno@chromium.org>
Thu, 2 Jul 2015 06:20:03 +0000 (23:20 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 2 Jul 2015 06:20:14 +0000 (06:20 +0000)
This involves:
- Enabling the tail call optimization reducer in all cases.
- Adding an addition flag to CallFunctionParameters to mark call sites
  that can be tail-called enabled.
- Only set the tail-call flag for %_CallFunction.

R=bmeurer@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#29436}

21 files changed:
src/compiler/arm/code-generator-arm.cc
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/js-generic-lowering.cc
src/compiler/js-intrinsic-lowering.cc
src/compiler/js-intrinsic-lowering.h
src/compiler/js-operator.cc
src/compiler/js-operator.h
src/compiler/linkage.cc
src/compiler/linkage.h
src/compiler/mips/code-generator-mips.cc
src/compiler/mips/instruction-selector-mips.cc
src/compiler/mips64/code-generator-mips64.cc
src/compiler/mips64/instruction-selector-mips64.cc
src/compiler/pipeline.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-selector-x64.cc
test/mjsunit/call-runtime-tail.js [new file with mode: 0644]

index b9cc354b2321cd70d74670396661ce486ee8d923..2b605460787f8a2846b6bd8af58abc3c6e7ee69d 100644 (file)
@@ -304,10 +304,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   int stack_slots = frame()->GetSpillSlotCount();
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ LeaveFrame(StackFrame::MANUAL);
-    int pop_count = descriptor->IsJSFunctionCall()
-                        ? static_cast<int>(descriptor->JSParameterCount())
-                        : 0;
-    __ Drop(pop_count);
   }
 }
 
@@ -1053,8 +1049,12 @@ void CodeGenerator::AssembleReturn() {
       __ LeaveFrame(StackFrame::MANUAL);
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ Drop(pop_count);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count != 0) {
+        __ Drop(pop_count);
+      }
       __ Ret();
     }
   } else {
index b5324544600c506a69813c309e691a49e3d8bb9d..d9f8f8b1de5bdf92010aaff3c02f4d759909da0a 100644 (file)
@@ -1166,9 +1166,7 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
@@ -1177,8 +1175,6 @@ void InstructionSelector::VisitTailCall(Node* node) {
     // heuristics in the register allocator for where to emit constants.
     InitializeCallBuffer(node, &buffer, true, false);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
index 0dab3c7434e1418657e2af72a78edca673059998..c3e9af6a29e7e6be9931bf1872eebdd96290aa75 100644 (file)
@@ -351,10 +351,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ Mov(jssp, fp);
     __ Pop(fp, lr);
-    int pop_count = descriptor->IsJSFunctionCall()
-                        ? static_cast<int>(descriptor->JSParameterCount())
-                        : 0;
-    __ Drop(pop_count);
   }
 }
 
@@ -1184,8 +1180,12 @@ void CodeGenerator::AssembleReturn() {
       __ Pop(fp, lr);
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ Drop(pop_count);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count != 0) {
+        __ Drop(pop_count);
+      }
       __ Ret();
     }
   } else {
index 3eb37e1ab87cd93f8b6add3e8f678b85141bd970..b304abcc4915a10c17206ca256ec3c517d0da0a6 100644 (file)
@@ -1470,9 +1470,7 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
@@ -1481,8 +1479,6 @@ void InstructionSelector::VisitTailCall(Node* node) {
     // heuristics in the register allocator for where to emit constants.
     InitializeCallBuffer(node, &buffer, true, false);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
index 7272fdee9a3776a9902ffd4f2a375514e8bfd61c..4690a8cc05d869893778ed9f12c229b2b10f88c5 100644 (file)
@@ -290,13 +290,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ mov(esp, ebp);
     __ pop(ebp);
-    int32_t bytes_to_pop =
-        descriptor->IsJSFunctionCall()
-            ? static_cast<int32_t>(descriptor->JSParameterCount() *
-                                   kPointerSize)
-            : 0;
-    __ pop(Operand(esp, bytes_to_pop));
-    __ add(esp, Immediate(bytes_to_pop));
   }
 }
 
@@ -1348,8 +1341,14 @@ void CodeGenerator::AssembleReturn() {
       __ pop(ebp);       // Pop caller's frame pointer.
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ Ret(pop_count * kPointerSize, ebx);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count == 0) {
+        __ ret(0);
+      } else {
+        __ Ret(pop_count * kPointerSize, ebx);
+      }
     }
   } else {
     __ ret(0);
index 2720910ca3786678b4e5a52f1b5cf1031876a57b..d9ff422597f0738d117af20ca40cb121e21b9113 100644 (file)
@@ -906,16 +906,13 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
     InitializeCallBuffer(node, &buffer, true, true);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
index d907c3d14ffa883569fe25d785101a96a460662c..da42aba523c3f635bf22e3caedf2b5c17497410b 100644 (file)
@@ -529,6 +529,9 @@ void JSGenericLowering::LowerJSCallFunction(Node* node) {
   CallFunctionStub stub(isolate(), arg_count, p.flags());
   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
   CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
+  if (p.AllowTailCalls()) {
+    flags |= CallDescriptor::kSupportsTailCalls;
+  }
   CallDescriptor* desc = Linkage::GetStubCallDescriptor(
       isolate(), zone(), d, static_cast<int>(p.arity() - 1), flags);
   Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
index 0039d1d4c3ee9f89ca80dc6d6be8abdc1b8eb9de..01552559ea39b83a7c4f62f41ecb743eb2fccb90 100644 (file)
@@ -94,6 +94,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
       return ReduceGetCallerJSFunction(node);
     case Runtime::kInlineThrowNotDateError:
       return ReduceThrowNotDateError(node);
+    case Runtime::kInlineCallFunction:
+      return ReduceCallFunction(node);
     default:
       break;
   }
@@ -513,6 +515,21 @@ Reduction JSIntrinsicLowering::ReduceThrowNotDateError(Node* node) {
 }
 
 
+Reduction JSIntrinsicLowering::ReduceCallFunction(Node* node) {
+  CallRuntimeParameters params = OpParameter<CallRuntimeParameters>(node->op());
+  size_t arity = params.arity();
+  node->set_op(javascript()->CallFunction(arity, NO_CALL_FUNCTION_FLAGS, STRICT,
+                                          VectorSlotPair(), ALLOW_TAIL_CALLS));
+  Node* function = node->InputAt(static_cast<int>(arity - 1));
+  while (--arity != 0) {
+    node->ReplaceInput(static_cast<int>(arity),
+                       node->InputAt(static_cast<int>(arity - 1)));
+  }
+  node->ReplaceInput(0, function);
+  return Changed(node);
+}
+
+
 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
                                       Node* b) {
   node->set_op(op);
@@ -549,6 +566,10 @@ CommonOperatorBuilder* JSIntrinsicLowering::common() const {
   return jsgraph()->common();
 }
 
+JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
+  return jsgraph_->javascript();
+}
+
 
 MachineOperatorBuilder* JSIntrinsicLowering::machine() const {
   return jsgraph()->machine();
index 5dd46156b3c587ed95dfc7c74b7a8b45143710b2..61cf9c4817dfcdbeb03d38cf81d58db55c6cdebd 100644 (file)
@@ -15,6 +15,7 @@ namespace compiler {
 
 // Forward declarations.
 class CommonOperatorBuilder;
+class JSOperatorBuilder;
 class JSGraph;
 class MachineOperatorBuilder;
 
@@ -56,6 +57,7 @@ class JSIntrinsicLowering final : public AdvancedReducer {
   Reduction ReduceGetTypeFeedbackVector(Node* node);
   Reduction ReduceGetCallerJSFunction(Node* node);
   Reduction ReduceThrowNotDateError(Node* node);
+  Reduction ReduceCallFunction(Node* node);
 
   Reduction Change(Node* node, const Operator* op);
   Reduction Change(Node* node, const Operator* op, Node* a, Node* b);
@@ -65,6 +67,7 @@ class JSIntrinsicLowering final : public AdvancedReducer {
   Graph* graph() const;
   JSGraph* jsgraph() const { return jsgraph_; }
   CommonOperatorBuilder* common() const;
+  JSOperatorBuilder* javascript() const;
   MachineOperatorBuilder* machine() const;
   DeoptimizationMode mode() const { return mode_; }
   SimplifiedOperatorBuilder* simplified() { return &simplified_; }
index d496dff3c389cdae29954dc1db97721f0eff732c..1966724a86a1ab2df7a5cfe5579e79ee51e70e3b 100644 (file)
@@ -30,7 +30,11 @@ size_t hash_value(VectorSlotPair const& p) {
 
 
 std::ostream& operator<<(std::ostream& os, CallFunctionParameters const& p) {
-  return os << p.arity() << ", " << p.flags() << ", " << p.language_mode();
+  os << p.arity() << ", " << p.flags() << ", " << p.language_mode();
+  if (p.AllowTailCalls()) {
+    os << ", ALLOW_TAIL_CALLS";
+  }
+  return os;
 }
 
 
@@ -470,10 +474,13 @@ CACHED_OP_LIST_WITH_LANGUAGE_MODE(CACHED_WITH_LANGUAGE_MODE)
 #undef CACHED_WITH_LANGUAGE_MODE
 
 
-const Operator* JSOperatorBuilder::CallFunction(
-    size_t arity, CallFunctionFlags flags, LanguageMode language_mode,
-    VectorSlotPair const& feedback) {
-  CallFunctionParameters parameters(arity, flags, language_mode, feedback);
+const Operator* JSOperatorBuilder::CallFunction(size_t arity,
+                                                CallFunctionFlags flags,
+                                                LanguageMode language_mode,
+                                                VectorSlotPair const& feedback,
+                                                TailCallMode tail_call_mode) {
+  CallFunctionParameters parameters(arity, flags, language_mode, feedback,
+                                    tail_call_mode);
   return new (zone()) Operator1<CallFunctionParameters>(   // --
       IrOpcode::kJSCallFunction, Operator::kNoProperties,  // opcode
       "JSCallFunction",                                    // name
index 3a7d2ba9157957ec24a78e354e3693edd9404706..d70c8e2096186a4dc8577985d2cf74d4858a3851 100644 (file)
@@ -45,6 +45,7 @@ bool operator!=(VectorSlotPair const&, VectorSlotPair const&);
 
 size_t hash_value(VectorSlotPair const&);
 
+enum TailCallMode { NO_TAIL_CALLS, ALLOW_TAIL_CALLS };
 
 // Defines the arity and the call flags for a JavaScript function call. This is
 // used as a parameter by JSCallFunction operators.
@@ -52,10 +53,12 @@ class CallFunctionParameters final {
  public:
   CallFunctionParameters(size_t arity, CallFunctionFlags flags,
                          LanguageMode language_mode,
-                         VectorSlotPair const& feedback)
+                         VectorSlotPair const& feedback,
+                         TailCallMode tail_call_mode)
       : bit_field_(ArityField::encode(arity) | FlagsField::encode(flags) |
                    LanguageModeField::encode(language_mode)),
-        feedback_(feedback) {}
+        feedback_(feedback),
+        tail_call_mode_(tail_call_mode) {}
 
   size_t arity() const { return ArityField::decode(bit_field_); }
   CallFunctionFlags flags() const { return FlagsField::decode(bit_field_); }
@@ -72,6 +75,8 @@ class CallFunctionParameters final {
     return !(*this == that);
   }
 
+  bool AllowTailCalls() const { return tail_call_mode_ == ALLOW_TAIL_CALLS; }
+
  private:
   friend size_t hash_value(CallFunctionParameters const& p) {
     return base::hash_combine(p.bit_field_, p.feedback_);
@@ -83,6 +88,7 @@ class CallFunctionParameters final {
 
   const uint32_t bit_field_;
   const VectorSlotPair feedback_;
+  bool tail_call_mode_;
 };
 
 size_t hash_value(CallFunctionParameters const&);
@@ -415,7 +421,8 @@ class JSOperatorBuilder final : public ZoneObject {
 
   const Operator* CallFunction(
       size_t arity, CallFunctionFlags flags, LanguageMode language_mode,
-      VectorSlotPair const& feedback = VectorSlotPair());
+      VectorSlotPair const& feedback = VectorSlotPair(),
+      TailCallMode tail_call_mode = NO_TAIL_CALLS);
   const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
 
   const Operator* CallConstruct(int arguments);
index 9338a5bed7029d4783618b9734df6fb06bcd0a4e..93bf21a84291918f40b2d63b65f6a52f9aa6e083 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "src/code-stubs.h"
 #include "src/compiler.h"
+#include "src/compiler/common-operator.h"
 #include "src/compiler/linkage.h"
 #include "src/compiler/node.h"
 #include "src/compiler/pipeline.h"
@@ -48,6 +49,63 @@ bool CallDescriptor::HasSameReturnLocationsAs(
 }
 
 
+bool CallDescriptor::CanTailCall(const Node* node) const {
+  // 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);
+  if (!HasSameReturnLocationsAs(other)) return false;
+  size_t current_input = 0;
+  size_t other_input = 0;
+  size_t stack_parameter = 0;
+  while (true) {
+    if (other_input >= other->InputCount()) {
+      while (current_input <= InputCount()) {
+        if (!GetInputLocation(current_input).is_register()) {
+          return false;
+        }
+        ++current_input;
+      }
+      return true;
+    }
+    if (current_input >= InputCount()) {
+      while (other_input < other->InputCount()) {
+        if (!other->GetInputLocation(other_input).is_register()) {
+          return false;
+        }
+        ++other_input;
+      }
+      return true;
+    }
+    if (GetInputLocation(current_input).is_register()) {
+      ++current_input;
+      continue;
+    }
+    if (other->GetInputLocation(other_input).is_register()) {
+      ++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;
+    }
+    size_t param_index = ParameterIndexOf(input->op());
+    if (param_index != stack_parameter) {
+      return false;
+    }
+    ++stack_parameter;
+    ++current_input;
+    ++other_input;
+  }
+  UNREACHABLE();
+  return false;
+}
+
+
 CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) {
   if (info->code_stub() != NULL) {
     // Use the code stub interface descriptor.
index ea130d7ddf1df33cd1d497f3a391864732720e99..e403f63bb14ac890ded2dd295d1131538627a734 100644 (file)
@@ -20,6 +20,7 @@ class CallInterfaceDescriptor;
 
 namespace compiler {
 
+class Node;
 class OsrHelper;
 
 // Describes the location for a parameter or a return value to a call.
@@ -167,6 +168,8 @@ class CallDescriptor final : public ZoneObject {
 
   bool HasSameReturnLocationsAs(const CallDescriptor* other) const;
 
+  bool CanTailCall(const Node* call) const;
+
  private:
   friend class Linkage;
 
index a7806ccae236c2ee0e83399386915b1480f9d299..4d87f2c2434c331db521d7b71e07db2d80bf6fc0 100644 (file)
@@ -399,10 +399,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   int stack_slots = frame()->GetSpillSlotCount();
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ LeaveFrame(StackFrame::MANUAL);
-    int pop_count = descriptor->IsJSFunctionCall()
-                        ? static_cast<int>(descriptor->JSParameterCount())
-                        : 0;
-    __ Drop(pop_count);
   }
 }
 
@@ -1157,8 +1153,14 @@ void CodeGenerator::AssembleReturn() {
       __ Pop(ra, fp);
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ DropAndRet(pop_count);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count != 0) {
+        __ DropAndRet(pop_count);
+      } else {
+        __ Ret();
+      }
     }
   } else {
     __ Ret();
index 8ecc25e9e084951baa2c6d1626e18a894a5cd67d..52be8a9a2a5b81cf4ab971d6dc5911082626330b 100644 (file)
@@ -589,16 +589,12 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
     InitializeCallBuffer(node, &buffer, true, false);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
index 534ebfb09e5da459388d612f33a4008f2bb62b74..b3f8088e55f1403308399881dce3a64723a8c4c0 100644 (file)
@@ -399,10 +399,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   int stack_slots = frame()->GetSpillSlotCount();
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ LeaveFrame(StackFrame::MANUAL);
-    int pop_count = descriptor->IsJSFunctionCall()
-                        ? static_cast<int>(descriptor->JSParameterCount())
-                        : 0;
-    __ Drop(pop_count);
   }
 }
 
@@ -1230,8 +1226,14 @@ void CodeGenerator::AssembleReturn() {
       __ Pop(ra, fp);
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ DropAndRet(pop_count);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count != 0) {
+        __ DropAndRet(pop_count);
+      } else {
+        __ Ret();
+      }
     }
   } else {
     __ Ret();
index 4127c312f21f8e777b4d0f1b4e1503a6d0c3a68e..d4dbfe03af4cbceebac1a36840989506b657721b 100644 (file)
@@ -738,16 +738,12 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
     InitializeCallBuffer(node, &buffer, true, false);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
index 6f85651a2e93e8882b178d03a2e865d965870b2d..7ced8e67b261d83606059dbc4192dbdb6d4ada4a 100644 (file)
@@ -703,8 +703,7 @@ struct GenericLoweringPhase {
     AddReducer(data, &graph_reducer, &common_reducer);
     AddReducer(data, &graph_reducer, &generic_lowering);
     AddReducer(data, &graph_reducer, &select_lowering);
-    // TODO(turbofan): TCO is currently limited to stubs.
-    if (data->info()->IsStub()) AddReducer(data, &graph_reducer, &tco);
+    AddReducer(data, &graph_reducer, &tco);
     graph_reducer.ReduceGraph();
   }
 };
index 50eb64cceddfa826641e69d36600cd757b1daa49..bdce0832011e4ce8a1c21a301c1672535e7f7171 100644 (file)
@@ -538,13 +538,6 @@ void CodeGenerator::AssembleDeconstructActivationRecord() {
   if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
     __ movq(rsp, rbp);
     __ popq(rbp);
-    int32_t bytes_to_pop =
-        descriptor->IsJSFunctionCall()
-            ? static_cast<int32_t>(descriptor->JSParameterCount() *
-                                   kPointerSize)
-            : 0;
-    __ popq(Operand(rsp, bytes_to_pop));
-    __ addq(rsp, Immediate(bytes_to_pop));
   }
 }
 
@@ -1578,11 +1571,17 @@ void CodeGenerator::AssembleReturn() {
       __ popq(rbp);       // Pop caller's frame pointer.
       int pop_count = descriptor->IsJSFunctionCall()
                           ? static_cast<int>(descriptor->JSParameterCount())
-                          : 0;
-      __ Ret(pop_count * kPointerSize, rbx);
+                          : (info()->IsStub()
+                                 ? info()->code_stub()->GetStackParameterCount()
+                                 : 0);
+      if (pop_count == 0) {
+        __ Ret();
+      } else {
+        __ Ret(pop_count * kPointerSize, rbx);
+      }
     }
   } else {
-    __ ret(0);
+    __ Ret();
   }
 }
 
index 35785c37a8ebcb8a7230ef330741fdbf19690c6e..b245fa8f879ec77a174d27c9de37f184e864f070 100644 (file)
@@ -1112,16 +1112,12 @@ void InstructionSelector::VisitTailCall(Node* node) {
   DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
 
   // TODO(turbofan): Relax restriction for stack parameters.
-  if (descriptor->UsesOnlyRegisters() &&
-      descriptor->HasSameReturnLocationsAs(
-          linkage()->GetIncomingDescriptor())) {
+  if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
     CallBuffer buffer(zone(), descriptor, nullptr);
 
     // Compute InstructionOperands for inputs and outputs.
     InitializeCallBuffer(node, &buffer, true, true);
 
-    DCHECK_EQ(0u, buffer.pushed_nodes.size());
-
     // Select the appropriate opcode based on the call type.
     InstructionCode opcode;
     switch (descriptor->kind()) {
diff --git a/test/mjsunit/call-runtime-tail.js b/test/mjsunit/call-runtime-tail.js
new file mode 100644 (file)
index 0000000..6ad107d
--- /dev/null
@@ -0,0 +1,81 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --nostress-opt --turbo
+
+var p0 = new Object();
+var p1 = new Object();
+var p2 = new Object();
+
+// Ensure 1 parameter passed straight-through is handled correctly
+var count1 = 100000;
+tailee1 = function() {
+  "use strict";
+  if (count1-- == 0) {
+    return this;
+  }
+  return %_CallFunction(this, tailee1);
+};
+
+%OptimizeFunctionOnNextCall(tailee1);
+assertEquals(p0, tailee1.call(p0));
+
+// Ensure 2 parameters passed straight-through trigger a tail call are handled
+// correctly and don't cause a stack overflow.
+var count2 = 100000;
+tailee2 = function(px) {
+  "use strict";
+  assertEquals(p2, px);
+  assertEquals(p1, this);
+  count2 = ((count2 | 0) - 1) | 0;
+  if ((count2 | 0) === 0) {
+    return this;
+  }
+  return %_CallFunction(this, px, tailee2);
+};
+
+%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;
+tailee3 = function(px) {
+  "use strict";
+  if (count3-- == 0) {
+    return this;
+  }
+  return %_CallFunction(px, this, tailee3);
+};
+
+%OptimizeFunctionOnNextCall(tailee3);
+assertThrows(function() { tailee3.call(p1, p2); });
+
+// Ensure too many parameters defeats the tail call optimization (currently
+// unsupported).
+var count4 = 1000000;
+tailee4 = function(px) {
+  "use strict";
+  if (count4-- == 0) {
+    return this;
+  }
+  return %_CallFunction(this, px, undefined, tailee4);
+};
+
+%OptimizeFunctionOnNextCall(tailee4);
+assertThrows(function() { tailee4.call(p1, p2); });
+
+// Ensure too few parameters defeats the tail call optimization (currently
+// unsupported).
+var count5 = 1000000;
+tailee5 = function(px) {
+  "use strict";
+  if (count5-- == 0) {
+    return this;
+  }
+  return %_CallFunction(this, tailee5);
+};
+
+%OptimizeFunctionOnNextCall(tailee5);
+assertThrows(function() { tailee5.call(p1, p2); });