[Interpreter] Add support for parameter variables.
authorrmcilroy <rmcilroy@chromium.org>
Thu, 27 Aug 2015 10:32:26 +0000 (03:32 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 27 Aug 2015 10:32:39 +0000 (10:32 +0000)
Adds support for parameters to the BytecodeArrayBuilder and BytecodeGenerator.
Parameters are accessed as negative interpreter registers.

BUG=v8:4280
LOG=N

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

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

21 files changed:
src/arm/builtins-arm.cc
src/arm64/builtins-arm64.cc
src/factory.cc
src/factory.h
src/frames.h
src/heap/heap.cc
src/heap/heap.h
src/ia32/builtins-ia32.cc
src/interpreter/bytecode-array-builder.cc
src/interpreter/bytecode-array-builder.h
src/interpreter/bytecode-generator.cc
src/mips/builtins-mips.cc
src/mips64/builtins-mips64.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/x64/builtins-x64.cc
test/cctest/interpreter/test-bytecode-generator.cc
test/cctest/interpreter/test-interpreter.cc
test/cctest/test-heap.cc
test/unittests/interpreter/bytecode-array-builder-unittest.cc

index 3902584..9186c35 100644 (file)
@@ -1003,8 +1003,11 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ LeaveFrame(StackFrame::JAVA_SCRIPT);
-  // Drop receiver + arguments.
-  __ Drop(1);  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
+
+  // Drop receiver + arguments and return.
+  __ ldr(ip, FieldMemOperand(kInterpreterBytecodeArrayRegister,
+                             BytecodeArray::kParameterSizeOffset));
+  __ add(sp, sp, ip, LeaveCC);
   __ Jump(lr);
 }
 
index 3b568b4..3c51f50 100644 (file)
@@ -1025,9 +1025,11 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ LeaveFrame(StackFrame::JAVA_SCRIPT);
-  // Drop receiver + arguments.
-  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
-  __ Drop(1, kXRegSize);
+
+  // Drop receiver + arguments and return.
+  __ Ldr(w1, FieldMemOperand(kInterpreterBytecodeArrayRegister,
+                             BytecodeArray::kParameterSizeOffset));
+  __ Drop(x1, 1);
   __ Ret();
 }
 
index 1993965..8850861 100644 (file)
@@ -889,10 +889,12 @@ Handle<ByteArray> Factory::NewByteArray(int length, PretenureFlag pretenure) {
 
 Handle<BytecodeArray> Factory::NewBytecodeArray(int length,
                                                 const byte* raw_bytecodes,
-                                                int frame_size) {
+                                                int frame_size,
+                                                int parameter_count) {
   DCHECK(0 <= length);
-  CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->AllocateBytecodeArray(
-                                    length, raw_bytecodes, frame_size),
+  CALL_HEAP_FUNCTION(isolate(),
+                     isolate()->heap()->AllocateBytecodeArray(
+                         length, raw_bytecodes, frame_size, parameter_count),
                      BytecodeArray);
 }
 
index aa49b99..bfca11e 100644 (file)
@@ -288,7 +288,7 @@ class Factory final {
                                  PretenureFlag pretenure = NOT_TENURED);
 
   Handle<BytecodeArray> NewBytecodeArray(int length, const byte* raw_bytecodes,
-                                         int frame_size);
+                                         int frame_size, int parameter_count);
 
   Handle<FixedTypedArrayBase> NewFixedTypedArrayWithExternalPointer(
       int length, ExternalArrayType array_type, void* external_pointer,
index fecaec6..05c0a35 100644 (file)
@@ -174,6 +174,14 @@ class ConstructFrameConstants : public AllStatic {
 };
 
 
+class InterpreterFrameConstants : public AllStatic {
+ public:
+  // Register file pointer relative.
+  static const int kLastParamFromRegisterPointer =
+      StandardFrameConstants::kFixedFrameSize + kPointerSize;
+};
+
+
 // Abstract base class for all stack frames.
 class StackFrame BASE_EMBEDDED {
  public:
index accae68..ca50fdd 100644 (file)
@@ -2924,8 +2924,7 @@ bool Heap::CreateInitialMaps() {
       set_empty_byte_array(byte_array);
 
       BytecodeArray* bytecode_array;
-      AllocationResult allocation =
-          AllocateBytecodeArray(0, nullptr, kPointerSize);
+      AllocationResult allocation = AllocateBytecodeArray(0, nullptr, 0, 0);
       if (!allocation.To(&bytecode_array)) {
         return false;
       }
@@ -3524,7 +3523,8 @@ AllocationResult Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
 
 AllocationResult Heap::AllocateBytecodeArray(int length,
                                              const byte* const raw_bytecodes,
-                                             int frame_size) {
+                                             int frame_size,
+                                             int parameter_count) {
   if (length < 0 || length > BytecodeArray::kMaxLength) {
     v8::internal::Heap::FatalProcessOutOfMemory("invalid array length", true);
   }
@@ -3540,6 +3540,7 @@ AllocationResult Heap::AllocateBytecodeArray(int length,
   BytecodeArray* instance = BytecodeArray::cast(result);
   instance->set_length(length);
   instance->set_frame_size(frame_size);
+  instance->set_parameter_count(parameter_count);
   CopyBytes(instance->GetFirstBytecodeAddress(), raw_bytecodes, length);
 
   return result;
index a529080..ea99fd8 100644 (file)
@@ -1996,7 +1996,8 @@ class Heap {
 
   // Allocates a bytecode array with given contents.
   MUST_USE_RESULT AllocationResult
-  AllocateBytecodeArray(int length, const byte* raw_bytecodes, int frame_size);
+  AllocateBytecodeArray(int length, const byte* raw_bytecodes, int frame_size,
+                        int parameter_count);
 
   // Copy the code and scope info part of the code object, but insert
   // the provided data as the relocation information.
index 1b25af7..db3c6ef 100644 (file)
@@ -752,9 +752,14 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ leave();
-  // Return droping receiver + arguments.
-  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
-  __ Ret(1 * kPointerSize, ecx);
+
+  // Drop receiver + arguments and return.
+  __ mov(ebx, FieldOperand(kInterpreterBytecodeArrayRegister,
+                           BytecodeArray::kParameterSizeOffset));
+  __ pop(ecx);
+  __ add(esp, ebx);
+  __ push(ecx);
+  __ ret(0);
 }
 
 
index bb1c69b..ebab9f1 100644 (file)
@@ -11,6 +11,7 @@ namespace interpreter {
 BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate)
     : isolate_(isolate),
       bytecode_generated_(false),
+      parameter_count_(-1),
       local_register_count_(-1),
       temporary_register_count_(0),
       temporary_register_next_(0) {}
@@ -25,14 +26,30 @@ void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
 int BytecodeArrayBuilder::locals_count() const { return local_register_count_; }
 
 
+void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) {
+  parameter_count_ = number_of_parameters;
+}
+
+
+int BytecodeArrayBuilder::parameter_count() const { return parameter_count_; }
+
+
+Register BytecodeArrayBuilder::Parameter(int param_index) {
+  DCHECK_GE(param_index, 0);
+  DCHECK_LT(param_index, parameter_count_);
+  return Register(kLastParamRegisterIndex - parameter_count_ + param_index + 1);
+}
+
+
 Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
   DCHECK_EQ(bytecode_generated_, false);
+  DCHECK_GE(parameter_count_, 0);
   DCHECK_GE(local_register_count_, 0);
   int bytecode_size = static_cast<int>(bytecodes_.size());
   int register_count = local_register_count_ + temporary_register_count_;
   int frame_size = register_count * kPointerSize;
   Handle<BytecodeArray> output = isolate_->factory()->NewBytecodeArray(
-      bytecode_size, &bytecodes_.front(), frame_size);
+      bytecode_size, &bytecodes_.front(), frame_size, parameter_count_);
   bytecode_generated_ = true;
   return output;
 }
@@ -135,9 +152,12 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
       return false;
     case OperandType::kImm8:
       return true;
-    case OperandType::kReg:
-      return Register::FromOperand(operand_value).index() <
-             temporary_register_next_;
+    case OperandType::kReg: {
+      int reg_index = Register::FromOperand(operand_value).index();
+      return (reg_index >= 0 && reg_index < temporary_register_next_) ||
+             (reg_index <= kLastParamRegisterIndex &&
+              reg_index > kLastParamRegisterIndex - parameter_count_);
+    }
   }
   UNREACHABLE();
   return false;
index c4ab816..fda32e6 100644 (file)
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "src/ast.h"
+#include "src/frames.h"
 #include "src/interpreter/bytecodes.h"
 
 namespace v8 {
@@ -24,10 +25,16 @@ class BytecodeArrayBuilder {
   explicit BytecodeArrayBuilder(Isolate* isolate);
   Handle<BytecodeArray> ToBytecodeArray();
 
+  // Set number of parameters expected by function.
+  void set_parameter_count(int number_of_params);
+  int parameter_count() const;
+
   // Set number of locals required for bytecode array.
   void set_locals_count(int number_of_locals);
   int locals_count() const;
 
+  Register Parameter(int parameter_index);
+
   // Constant loads to accumulator.
   BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value);
   BytecodeArrayBuilder& LoadUndefined();
@@ -47,6 +54,9 @@ class BytecodeArrayBuilder {
   BytecodeArrayBuilder& Return();
 
  private:
+  static const int kLastParamRegisterIndex =
+      -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
+
   static Bytecode BytecodeForBinaryOperation(Token::Value op);
 
   void Output(Bytecode bytecode, uint8_t r0, uint8_t r1, uint8_t r2);
@@ -64,6 +74,7 @@ class BytecodeArrayBuilder {
   std::vector<uint8_t> bytecodes_;
   bool bytecode_generated_;
 
+  int parameter_count_;
   int local_register_count_;
   int temporary_register_count_;
   int temporary_register_next_;
@@ -72,17 +83,20 @@ class BytecodeArrayBuilder {
   DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodeArrayBuilder);
 };
 
-// An interpreter register which is located in the function's regsiter file
+// An interpreter register which is located in the function's register file
 // in its stack-frame.
 class Register {
  public:
   static const int kMaxRegisterIndex = 128;
+  static const int kMinRegisterIndex = -127;
 
   explicit Register(int index) : index_(index) {
     DCHECK_LE(index_, kMaxRegisterIndex);
+    DCHECK_GE(index_, kMinRegisterIndex);
   }
 
   int index() { return index_; }
+
   uint8_t ToOperand() { return static_cast<uint8_t>(-index_); }
   static Register FromOperand(uint8_t operand) {
     return Register(-static_cast<int8_t>(operand));
index 50f27bd..93d96ed 100644 (file)
@@ -30,6 +30,7 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
   // This a temporary guard (oth).
   DCHECK(scope()->is_function_scope());
 
+  builder().set_parameter_count(info->num_parameters_including_this());
   builder().set_locals_count(scope()->num_stack_slots());
 
   // Visit implicit declaration of the function name.
@@ -72,8 +73,6 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
       UNIMPLEMENTED();
       break;
     case VariableLocation::PARAMETER:
-      UNIMPLEMENTED();
-      break;
     case VariableLocation::LOCAL:
       // Details stored in scope, i.e. variable index.
       break;
@@ -248,9 +247,15 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
       builder().LoadAccumulatorWithRegister(source);
       break;
     }
+    case VariableLocation::PARAMETER: {
+      // The parameter indices are shifted by 1 (receiver is variable
+      // index -1 but is parameter index 0 in BytecodeArrayBuilder).
+      Register source(builder().Parameter(variable->index() + 1));
+      builder().LoadAccumulatorWithRegister(source);
+      break;
+    }
     case VariableLocation::GLOBAL:
     case VariableLocation::UNALLOCATED:
-    case VariableLocation::PARAMETER:
     case VariableLocation::CONTEXT:
     case VariableLocation::LOOKUP:
       UNIMPLEMENTED();
index a05c2e4..f642660 100644 (file)
@@ -8,7 +8,6 @@
 #include "src/debug/debug.h"
 #include "src/deoptimizer.h"
 #include "src/full-codegen/full-codegen.h"
-#include "src/interpreter/bytecodes.h"
 #include "src/runtime/runtime.h"
 
 
@@ -996,8 +995,11 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ LeaveFrame(StackFrame::JAVA_SCRIPT);
-  // Drop receiver + arguments.
-  __ Drop(1);  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
+
+  // Drop receiver + arguments and return.
+  __ lw(at, FieldMemOperand(kInterpreterBytecodeArrayRegister,
+                            BytecodeArray::kParameterSizeOffset));
+  __ Addu(sp, sp, at);
   __ Jump(ra);
 }
 
index 85ba4cb..1fc04a4 100644 (file)
@@ -993,8 +993,11 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ LeaveFrame(StackFrame::JAVA_SCRIPT);
-  // Drop receiver + arguments.
-  __ Drop(1);  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
+
+  // Drop receiver + arguments and return.
+  __ lw(at, FieldMemOperand(kInterpreterBytecodeArrayRegister,
+                            BytecodeArray::kParameterSizeOffset));
+  __ Daddu(sp, sp, at);
   __ Jump(ra);
 }
 
index d61f494..506e064 100644 (file)
@@ -4058,6 +4058,22 @@ int BytecodeArray::frame_size() const {
 }
 
 
+void BytecodeArray::set_parameter_count(int number_of_parameters) {
+  DCHECK_GE(number_of_parameters, 0);
+  // Parameter count is stored as the size on stack of the parameters to allow
+  // it to be used directly by generated code.
+  WRITE_INT_FIELD(this, kParameterSizeOffset,
+                  (number_of_parameters << kPointerSizeLog2));
+}
+
+
+int BytecodeArray::parameter_count() const {
+  // Parameter count is stored as the size on stack of the parameters to allow
+  // it to be used directly by generated code.
+  return READ_INT_FIELD(this, kParameterSizeOffset) >> kPointerSizeLog2;
+}
+
+
 Address BytecodeArray::GetFirstBytecodeAddress() {
   return reinterpret_cast<Address>(this) - kHeapObjectTag + kHeaderSize;
 }
index 26d6945..0430f2a 100644 (file)
@@ -11585,6 +11585,7 @@ void Code::Disassemble(const char* name, std::ostream& os) {  // NOLINT
 
 
 void BytecodeArray::Disassemble(std::ostream& os) {
+  os << "Parameter count " << parameter_count() << "\n";
   os << "Frame size " << frame_size() << "\n";
   Vector<char> buf = Vector<char>::New(50);
 
index ff226e5..f45bda2 100644 (file)
@@ -4112,9 +4112,13 @@ class BytecodeArray : public FixedArrayBase {
   // Returns data start address.
   inline Address GetFirstBytecodeAddress();
 
-  // Accessors for frame size and the number of locals
+  // Accessors for frame size.
   inline int frame_size() const;
-  inline void set_frame_size(int value);
+  inline void set_frame_size(int frame_size);
+
+  // Accessors for parameter count (including implicit 'this' receiver).
+  inline int parameter_count() const;
+  inline void set_parameter_count(int number_of_parameters);
 
   DECLARE_CAST(BytecodeArray)
 
@@ -4128,7 +4132,8 @@ class BytecodeArray : public FixedArrayBase {
 
   // Layout description.
   static const int kFrameSizeOffset = FixedArrayBase::kHeaderSize;
-  static const int kHeaderSize = kFrameSizeOffset + kIntSize;
+  static const int kParameterSizeOffset = kFrameSizeOffset + kIntSize;
+  static const int kHeaderSize = kParameterSizeOffset + kIntSize;
 
   static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize);
 
index a68238a..29f4732 100644 (file)
@@ -802,9 +802,14 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
 
   // Leave the frame (also dropping the register file).
   __ leave();
-  // Return droping receiver + arguments.
-  // TODO(rmcilroy): Get number of arguments from BytecodeArray.
-  __ Ret(1 * kPointerSize, rcx);
+
+  // Drop receiver + arguments and return.
+  __ movl(rbx, FieldOperand(kInterpreterBytecodeArrayRegister,
+                            BytecodeArray::kParameterSizeOffset));
+  __ PopReturnAddressTo(rcx);
+  __ addp(rsp, rbx);
+  __ PushReturnAddressFrom(rcx);
+  __ ret(0);
 }
 
 
index deb6c97..16dc3ee 100644 (file)
@@ -15,7 +15,7 @@ namespace interpreter {
 
 class BytecodeGeneratorHelper {
  public:
-  const char* kFunctionName = "my_function";
+  const char* kFunctionName = "f";
 
   BytecodeGeneratorHelper() {
     i::FLAG_ignition = true;
@@ -40,6 +40,12 @@ class BytecodeGeneratorHelper {
              kFunctionName);
     return MakeBytecode(program.start(), kFunctionName);
   }
+
+  Handle<BytecodeArray> MakeBytecodeForFunction(const char* function) {
+    ScopedVector<char> program(1024);
+    SNPrintF(program, "%s\n%s();", function, kFunctionName);
+    return MakeBytecode(program.start(), kFunctionName);
+  }
 };
 
 
@@ -47,6 +53,7 @@ class BytecodeGeneratorHelper {
 struct ExpectedSnippet {
   const char* body;
   int frame_size;
+  int parameter_count;
   int bytecode_length;
   const uint8_t bytecode[16];
 };
@@ -54,8 +61,8 @@ struct ExpectedSnippet {
 
 // Helper macros for handcrafting bytecode sequences.
 #define B(x) static_cast<uint8_t>(Bytecode::k##x)
-#define U8(x) static_cast<uint8_t>(x & 0xff)
-#define R(x) static_cast<uint8_t>(-x & 0xff)
+#define U8(x) static_cast<uint8_t>((x) & 0xff)
+#define R(x) static_cast<uint8_t>(-(x) & 0xff)
 
 
 TEST(PrimitiveReturnStatements) {
@@ -63,15 +70,15 @@ TEST(PrimitiveReturnStatements) {
   BytecodeGeneratorHelper helper;
 
   ExpectedSnippet snippets[] = {
-      {"return;", 0, 2, {B(LdaUndefined), B(Return)}},
-      {"return null;", 0, 2, {B(LdaNull), B(Return)}},
-      {"return true;", 0, 2, {B(LdaTrue), B(Return)}},
-      {"return false;", 0, 2, {B(LdaFalse), B(Return)}},
-      {"return 0;", 0, 2, {B(LdaZero), B(Return)}},
-      {"return +1;", 0, 3, {B(LdaSmi8), U8(1), B(Return)}},
-      {"return -1;", 0, 3, {B(LdaSmi8), U8(-1), B(Return)}},
-      {"return +127;", 0, 3, {B(LdaSmi8), U8(127), B(Return)}},
-      {"return -128;", 0, 3, {B(LdaSmi8), U8(-128), B(Return)}},
+      {"return;", 0, 1, 2, {B(LdaUndefined), B(Return)}},
+      {"return null;", 0, 1, 2, {B(LdaNull), B(Return)}},
+      {"return true;", 0, 1, 2, {B(LdaTrue), B(Return)}},
+      {"return false;", 0, 1, 2, {B(LdaFalse), B(Return)}},
+      {"return 0;", 0, 1, 2, {B(LdaZero), B(Return)}},
+      {"return +1;", 0, 1, 3, {B(LdaSmi8), U8(1), B(Return)}},
+      {"return -1;", 0, 1, 3, {B(LdaSmi8), U8(-1), B(Return)}},
+      {"return +127;", 0, 1, 3, {B(LdaSmi8), U8(127), B(Return)}},
+      {"return -128;", 0, 1, 3, {B(LdaSmi8), U8(-128), B(Return)}},
   };
 
   size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
@@ -79,6 +86,7 @@ TEST(PrimitiveReturnStatements) {
     Handle<BytecodeArray> ba =
         helper.MakeBytecodeForFunctionBody(snippets[i].body);
     CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
+    CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
     CHECK_EQ(ba->length(), snippets[i].bytecode_length);
     CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
                   ba->length()));
@@ -93,6 +101,7 @@ TEST(PrimitiveExpressions) {
   ExpectedSnippet snippets[] = {
       {"var x = 0; return x;",
        kPointerSize,
+       1,
        6,
        {
            B(LdaZero),     //
@@ -102,6 +111,7 @@ TEST(PrimitiveExpressions) {
        }},
       {"var x = 0; return x + 3;",
        2 * kPointerSize,
+       1,
        12,
        {
            B(LdaZero),         //
@@ -118,6 +128,38 @@ TEST(PrimitiveExpressions) {
     Handle<BytecodeArray> ba =
         helper.MakeBytecodeForFunctionBody(snippets[i].body);
     CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
+    CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
+    CHECK_EQ(ba->length(), snippets[i].bytecode_length);
+    CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
+                  ba->length()));
+  }
+}
+
+
+TEST(Parameters) {
+  InitializedHandleScope handle_scope;
+  BytecodeGeneratorHelper helper;
+
+  int last_param_index =
+      -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
+  ExpectedSnippet snippets[] = {
+      {"function f() { return this; }",
+       0, 1, 3, {B(Ldar), R(last_param_index), B(Return)}},
+      {"function f(arg1) { return arg1; }",
+       0, 2, 3, {B(Ldar), R(last_param_index), B(Return)}},
+      {"function f(arg1) { return this; }",
+       0, 2, 3, {B(Ldar), R(last_param_index - 1), B(Return)}},
+      {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return arg4; }",
+       0, 8, 3, {B(Ldar), R(last_param_index - 3), B(Return)}},
+      {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return this; }",
+       0, 8, 3, {B(Ldar), R(last_param_index - 7), B(Return)}}
+  };
+
+  size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
+  for (size_t i = 0; i < num_snippets; i++) {
+    Handle<BytecodeArray> ba = helper.MakeBytecodeForFunction(snippets[i].body);
+    CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
+    CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
     CHECK_EQ(ba->length(), snippets[i].bytecode_length);
     CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
                   ba->length()));
index 2c55885..26deea6 100644 (file)
@@ -14,16 +14,35 @@ namespace v8 {
 namespace internal {
 namespace interpreter {
 
+
+static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
+                                           Handle<JSFunction> function) {
+  return Execution::Call(isolate, function,
+                         isolate->factory()->undefined_value(), 0, nullptr,
+                         false);
+}
+
+
+template <class... A>
+static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
+                                           Handle<JSFunction> function,
+                                           A... args) {
+  Handle<Object> argv[] = { args... };
+  return Execution::Call(isolate, function,
+                         isolate->factory()->undefined_value(), sizeof...(args),
+                         argv, false);
+}
+
+
+template <class... A>
 class InterpreterCallable {
  public:
   InterpreterCallable(Isolate* isolate, Handle<JSFunction> function)
       : isolate_(isolate), function_(function) {}
   virtual ~InterpreterCallable() {}
 
-  MaybeHandle<Object> operator()() {
-    return Execution::Call(isolate_, function_,
-                           isolate_->factory()->undefined_value(), 0, nullptr,
-                           false);
+  MaybeHandle<Object> operator()(A... args) {
+    return CallInterpreter(isolate_, function_, args...);
   }
 
  private:
@@ -31,30 +50,39 @@ class InterpreterCallable {
   Handle<JSFunction> function_;
 };
 
+
 class InterpreterTester {
  public:
   InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode)
-      : isolate_(isolate), function_(GetBytecodeFunction(isolate, bytecode)) {
+      : isolate_(isolate), bytecode_(bytecode) {
     i::FLAG_ignition = true;
     // Ensure handler table is generated.
     isolate->interpreter()->Initialize();
   }
   virtual ~InterpreterTester() {}
 
-  InterpreterCallable GetCallable() {
-    return InterpreterCallable(isolate_, function_);
+  template <class... A>
+  InterpreterCallable<A...> GetCallable() {
+    return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
   }
 
  private:
   Isolate* isolate_;
-  Handle<JSFunction> function_;
+  Handle<BytecodeArray> bytecode_;
+
+  template <class... A>
+  Handle<JSFunction> GetBytecodeFunction() {
+    int arg_count = sizeof...(A);
+    std::string function_text("(function(");
+    for (int i = 0; i < arg_count; i++) {
+      function_text += i == 0 ? "a" : ", a";
+    }
+    function_text += "){})";
 
-  static Handle<JSFunction> GetBytecodeFunction(
-      Isolate* isolate, Handle<BytecodeArray> bytecode_array) {
     Handle<JSFunction> function = v8::Utils::OpenHandle(
-        *v8::Handle<v8::Function>::Cast(CompileRun("(function(){})")));
-    function->ReplaceCode(*isolate->builtins()->InterpreterEntryTrampoline());
-    function->shared()->set_function_data(*bytecode_array);
+        *v8::Handle<v8::Function>::Cast(CompileRun(function_text.c_str())));
+    function->ReplaceCode(*isolate_->builtins()->InterpreterEntryTrampoline());
+    function->shared()->set_function_data(*bytecode_);
     return function;
   }
 
@@ -72,127 +100,135 @@ using v8::internal::Smi;
 using v8::internal::Token;
 using namespace v8::internal::interpreter;
 
-TEST(TestInterpreterReturn) {
+TEST(InterpreterReturn) {
   InitializedHandleScope handles;
   Handle<Object> undefined_value =
       handles.main_isolate()->factory()->undefined_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(undefined_value));
 }
 
 
-TEST(TestInterpreterLoadUndefined) {
+TEST(InterpreterLoadUndefined) {
   InitializedHandleScope handles;
   Handle<Object> undefined_value =
       handles.main_isolate()->factory()->undefined_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.LoadUndefined().Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(undefined_value));
 }
 
 
-TEST(TestInterpreterLoadNull) {
+TEST(InterpreterLoadNull) {
   InitializedHandleScope handles;
   Handle<Object> null_value = handles.main_isolate()->factory()->null_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.LoadNull().Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(null_value));
 }
 
 
-TEST(TestInterpreterLoadTheHole) {
+TEST(InterpreterLoadTheHole) {
   InitializedHandleScope handles;
   Handle<Object> the_hole_value =
       handles.main_isolate()->factory()->the_hole_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.LoadTheHole().Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(the_hole_value));
 }
 
 
-TEST(TestInterpreterLoadTrue) {
+TEST(InterpreterLoadTrue) {
   InitializedHandleScope handles;
   Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.LoadTrue().Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(true_value));
 }
 
 
-TEST(TestInterpreterLoadFalse) {
+TEST(InterpreterLoadFalse) {
   InitializedHandleScope handles;
   Handle<Object> false_value = handles.main_isolate()->factory()->false_value();
 
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(0);
+  builder.set_parameter_count(1);
   builder.LoadFalse().Return();
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK(return_val.is_identical_to(false_value));
 }
 
 
-TEST(TestInterpreterLoadLiteral) {
+TEST(InterpreterLoadLiteral) {
   InitializedHandleScope handles;
   for (int i = -128; i < 128; i++) {
     BytecodeArrayBuilder builder(handles.main_isolate());
     builder.set_locals_count(0);
+    builder.set_parameter_count(1);
     builder.LoadLiteral(Smi::FromInt(i)).Return();
     Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
     InterpreterTester tester(handles.main_isolate(), bytecode_array);
-    InterpreterCallable callable(tester.GetCallable());
+    auto callable = tester.GetCallable<>();
     Handle<Object> return_val = callable().ToHandleChecked();
     CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(i));
   }
 }
 
 
-TEST(TestInterpreterLoadStoreRegisters) {
+TEST(InterpreterLoadStoreRegisters) {
   InitializedHandleScope handles;
   Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
   for (int i = 0; i <= Register::kMaxRegisterIndex; i++) {
     BytecodeArrayBuilder builder(handles.main_isolate());
     builder.set_locals_count(i + 1);
+    builder.set_parameter_count(1);
     Register reg(i);
     builder.LoadTrue()
         .StoreAccumulatorInRegister(reg)
@@ -202,19 +238,20 @@ TEST(TestInterpreterLoadStoreRegisters) {
     Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
     InterpreterTester tester(handles.main_isolate(), bytecode_array);
-    InterpreterCallable callable(tester.GetCallable());
+    auto callable = tester.GetCallable<>();
     Handle<Object> return_val = callable().ToHandleChecked();
     CHECK(return_val.is_identical_to(true_value));
   }
 }
 
 
-TEST(TestInterpreterAdd) {
+TEST(InterpreterAdd) {
   InitializedHandleScope handles;
   // TODO(rmcilroy): Do add tests for heap numbers and strings once we support
   // them.
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(1);
+  builder.set_parameter_count(1);
   Register reg(0);
   builder.LoadLiteral(Smi::FromInt(1))
       .StoreAccumulatorInRegister(reg)
@@ -224,17 +261,18 @@ TEST(TestInterpreterAdd) {
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
 }
 
 
-TEST(TestInterpreterSub) {
+TEST(InterpreterSub) {
   InitializedHandleScope handles;
   // TODO(rmcilroy): Do add tests for heap numbers once we support them.
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(1);
+  builder.set_parameter_count(1);
   Register reg(0);
   builder.LoadLiteral(Smi::FromInt(5))
       .StoreAccumulatorInRegister(reg)
@@ -244,17 +282,18 @@ TEST(TestInterpreterSub) {
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(-26));
 }
 
 
-TEST(TestInterpreterMul) {
+TEST(InterpreterMul) {
   InitializedHandleScope handles;
   // TODO(rmcilroy): Do add tests for heap numbers once we support them.
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(1);
+  builder.set_parameter_count(1);
   Register reg(0);
   builder.LoadLiteral(Smi::FromInt(111))
       .StoreAccumulatorInRegister(reg)
@@ -264,17 +303,18 @@ TEST(TestInterpreterMul) {
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(666));
 }
 
 
-TEST(TestInterpreterDiv) {
+TEST(InterpreterDiv) {
   InitializedHandleScope handles;
   // TODO(rmcilroy): Do add tests for heap numbers once we support them.
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(1);
+  builder.set_parameter_count(1);
   Register reg(0);
   builder.LoadLiteral(Smi::FromInt(-20))
       .StoreAccumulatorInRegister(reg)
@@ -284,17 +324,18 @@ TEST(TestInterpreterDiv) {
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(-4));
 }
 
 
-TEST(TestInterpreterMod) {
+TEST(InterpreterMod) {
   InitializedHandleScope handles;
   // TODO(rmcilroy): Do add tests for heap numbers once we support them.
   BytecodeArrayBuilder builder(handles.main_isolate());
   builder.set_locals_count(1);
+  builder.set_parameter_count(1);
   Register reg(0);
   builder.LoadLiteral(Smi::FromInt(121))
       .StoreAccumulatorInRegister(reg)
@@ -304,7 +345,66 @@ TEST(TestInterpreterMod) {
   Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
 
   InterpreterTester tester(handles.main_isolate(), bytecode_array);
-  InterpreterCallable callable(tester.GetCallable());
+  auto callable = tester.GetCallable<>();
   Handle<Object> return_val = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(21));
 }
+
+
+TEST(InterpreterParameter1) {
+  InitializedHandleScope handles;
+  BytecodeArrayBuilder builder(handles.main_isolate());
+  builder.set_locals_count(1);
+  builder.set_parameter_count(1);
+  builder.LoadAccumulatorWithRegister(builder.Parameter(0)).Return();
+  Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+
+  InterpreterTester tester(handles.main_isolate(), bytecode_array);
+  auto callable = tester.GetCallable<Handle<Object>>();
+
+  // Check for heap objects.
+  Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
+  Handle<Object> return_val = callable(true_value).ToHandleChecked();
+  CHECK(return_val.is_identical_to(true_value));
+
+  // Check for Smis.
+  return_val = callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate()))
+                   .ToHandleChecked();
+  CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
+}
+
+
+TEST(InterpreterParameter8) {
+  InitializedHandleScope handles;
+  BytecodeArrayBuilder builder(handles.main_isolate());
+  builder.set_locals_count(1);
+  builder.set_parameter_count(8);
+  builder.LoadAccumulatorWithRegister(builder.Parameter(0))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(1))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(2))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(3))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(4))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(5))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(6))
+      .BinaryOperation(Token::Value::ADD, builder.Parameter(7))
+      .Return();
+  Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+
+  InterpreterTester tester(handles.main_isolate(), bytecode_array);
+  typedef Handle<Object> H;
+  auto callable = tester.GetCallable<H, H, H, H, H, H, H, H>();
+
+  Handle<Smi> arg1 = Handle<Smi>(Smi::FromInt(1), handles.main_isolate());
+  Handle<Smi> arg2 = Handle<Smi>(Smi::FromInt(2), handles.main_isolate());
+  Handle<Smi> arg3 = Handle<Smi>(Smi::FromInt(3), handles.main_isolate());
+  Handle<Smi> arg4 = Handle<Smi>(Smi::FromInt(4), handles.main_isolate());
+  Handle<Smi> arg5 = Handle<Smi>(Smi::FromInt(5), handles.main_isolate());
+  Handle<Smi> arg6 = Handle<Smi>(Smi::FromInt(6), handles.main_isolate());
+  Handle<Smi> arg7 = Handle<Smi>(Smi::FromInt(7), handles.main_isolate());
+  Handle<Smi> arg8 = Handle<Smi>(Smi::FromInt(8), handles.main_isolate());
+  // Check for Smis.
+  Handle<Object> return_val =
+      callable(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
+          .ToHandleChecked();
+  CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(36));
+}
index 4d21031..d7193dc 100644 (file)
@@ -711,6 +711,7 @@ TEST(BytecodeArray) {
   static const uint8_t kRawBytes[] = {0xc3, 0x7e, 0xa5, 0x5a};
   static const int kRawBytesSize = sizeof(kRawBytes);
   static const int kFrameSize = 32;
+  static const int kParameterCount = 2;
 
   CcTest::InitializeVM();
   Isolate* isolate = CcTest::i_isolate();
@@ -719,12 +720,13 @@ TEST(BytecodeArray) {
   HandleScope scope(isolate);
 
   // Allocate and initialize BytecodeArray
-  Handle<BytecodeArray> array =
-      factory->NewBytecodeArray(kRawBytesSize, kRawBytes, kFrameSize);
+  Handle<BytecodeArray> array = factory->NewBytecodeArray(
+      kRawBytesSize, kRawBytes, kFrameSize, kParameterCount);
 
   CHECK(array->IsBytecodeArray());
   CHECK_EQ(array->length(), (int)sizeof(kRawBytes));
   CHECK_EQ(array->frame_size(), kFrameSize);
+  CHECK_EQ(array->parameter_count(), kParameterCount);
   CHECK_LE(array->address(), array->GetFirstBytecodeAddress());
   CHECK_GE(array->address() + array->BytecodeArraySize(),
            array->GetFirstBytecodeAddress() + array->length());
index 72af0c4..9b4d3ab 100644 (file)
@@ -22,6 +22,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
   BytecodeArrayBuilder builder(isolate());
 
   builder.set_locals_count(1);
+  builder.set_parameter_count(0);
   CHECK_EQ(builder.locals_count(), 1);
 
   // Emit constant loads.
@@ -79,6 +80,7 @@ TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
   for (int locals = 0; locals < 5; locals++) {
     for (int temps = 0; temps < 3; temps++) {
       BytecodeArrayBuilder builder(isolate());
+      builder.set_parameter_count(0);
       builder.set_locals_count(locals);
       builder.Return();
 
@@ -97,6 +99,7 @@ TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
 
 TEST_F(BytecodeArrayBuilderTest, TemporariesRecycled) {
   BytecodeArrayBuilder builder(isolate());
+  builder.set_parameter_count(0);
   builder.set_locals_count(0);
   builder.Return();
 
@@ -133,6 +136,17 @@ TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
   CHECK_EQ(actual_index, index);
 }
 
+
+TEST_F(BytecodeArrayBuilderTest, Parameters) {
+  BytecodeArrayBuilder builder(isolate());
+  builder.set_parameter_count(10);
+  builder.set_locals_count(0);
+
+  Register param0(builder.Parameter(0));
+  Register param9(builder.Parameter(9));
+  CHECK_EQ(param9.index() - param0.index(), 9);
+}
+
 }  // namespace interpreter
 }  // namespace internal
 }  // namespace v8