[Interpreter] Add interpreter support for compare ops and ToBoolean.
authoroth <oth@chromium.org>
Mon, 28 Sep 2015 18:05:56 +0000 (11:05 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 28 Sep 2015 18:07:05 +0000 (18:07 +0000)
The comparison operators and ToBoolean are implemented by calling into
the runtime. There are new runtime methods are prefixed with Interpreter
to make use case clear.

BUG=v8:4280
LOG=N

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

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

14 files changed:
BUILD.gn
src/compiler/bytecode-graph-builder.cc
src/interpreter/bytecode-array-builder.cc
src/interpreter/bytecodes.h
src/interpreter/interpreter.cc
src/objects.h
src/runtime/runtime-interpreter.cc [new file with mode: 0644]
src/runtime/runtime.h
test/cctest/interpreter/test-bytecode-generator.cc
test/cctest/interpreter/test-interpreter.cc
test/unittests/interpreter/bytecodes-unittest.cc
test/unittests/runtime/runtime-interpreter-unittest.cc [new file with mode: 0644]
test/unittests/unittests.gyp
tools/gyp/v8.gyp

index 6f60384..1d4f3b7 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1156,6 +1156,7 @@ source_set("v8_base") {
     "src/runtime/runtime-generator.cc",
     "src/runtime/runtime-i18n.cc",
     "src/runtime/runtime-internal.cc",
+    "src/runtime/runtime-interpreter.cc",
     "src/runtime/runtime-json.cc",
     "src/runtime/runtime-literals.cc",
     "src/runtime/runtime-liveedit.cc",
index 3158e8b..5ba18ff 100644 (file)
@@ -368,13 +368,13 @@ void BytecodeGraphBuilder::VisitTestGreaterThan(
 }
 
 
-void BytecodeGraphBuilder::VisitTestLessThanEqual(
+void BytecodeGraphBuilder::VisitTestLessThanOrEqual(
     const interpreter::BytecodeArrayIterator& iterator) {
   UNIMPLEMENTED();
 }
 
 
-void BytecodeGraphBuilder::VisitTestGreaterThanEqual(
+void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual(
     const interpreter::BytecodeArrayIterator& iterator) {
   UNIMPLEMENTED();
 }
index e0bc03c..9c6b590 100644 (file)
@@ -289,9 +289,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
       case Bytecode::kTestEqualStrict:
       case Bytecode::kTestNotEqualStrict:
       case Bytecode::kTestLessThan:
-      case Bytecode::kTestLessThanEqual:
+      case Bytecode::kTestLessThanOrEqual:
       case Bytecode::kTestGreaterThan:
-      case Bytecode::kTestGreaterThanEqual:
+      case Bytecode::kTestGreaterThanOrEqual:
       case Bytecode::kTestInstanceOf:
       case Bytecode::kTestIn:
         break;
@@ -560,9 +560,9 @@ Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
     case Token::Value::GT:
       return Bytecode::kTestGreaterThan;
     case Token::Value::LTE:
-      return Bytecode::kTestLessThanEqual;
+      return Bytecode::kTestLessThanOrEqual;
     case Token::Value::GTE:
-      return Bytecode::kTestGreaterThanEqual;
+      return Bytecode::kTestGreaterThanOrEqual;
     case Token::Value::INSTANCEOF:
       return Bytecode::kTestInstanceOf;
     case Token::Value::IN:
index 70be2f5..3862842 100644 (file)
@@ -68,8 +68,8 @@ namespace interpreter {
   V(TestNotEqualStrict, OperandType::kReg)                                 \
   V(TestLessThan, OperandType::kReg)                                       \
   V(TestGreaterThan, OperandType::kReg)                                    \
-  V(TestLessThanEqual, OperandType::kReg)                                  \
-  V(TestGreaterThanEqual, OperandType::kReg)                               \
+  V(TestLessThanOrEqual, OperandType::kReg)                                \
+  V(TestGreaterThanOrEqual, OperandType::kReg)                             \
   V(TestInstanceOf, OperandType::kReg)                                     \
   V(TestIn, OperandType::kReg)                                             \
                                                                            \
index f993724..2d97fc8 100644 (file)
@@ -294,15 +294,6 @@ void Interpreter::DoBinaryOp(Runtime::FunctionId function_id,
 }
 
 
-void Interpreter::DoCompareOp(Token::Value op,
-                              compiler::InterpreterAssembler* assembler) {
-  // TODO(oth): placeholder until compare path fixed.
-  // The accumulator should be set to true on success (or false otherwise)
-  // by the comparisons so it can be used for conditional jumps.
-  DoLdaTrue(assembler);
-}
-
-
 // Add <src>
 //
 // Add register <src> to accumulator.
@@ -363,7 +354,7 @@ void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
 //
 // Test if the value in the <src> register equals the accumulator.
 void Interpreter::DoTestEqual(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::EQ, assembler);
+  DoBinaryOp(Runtime::kInterpreterEquals, assembler);
 }
 
 
@@ -371,7 +362,7 @@ void Interpreter::DoTestEqual(compiler::InterpreterAssembler* assembler) {
 //
 // Test if the value in the <src> register is not equal to the accumulator.
 void Interpreter::DoTestNotEqual(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::NE, assembler);
+  DoBinaryOp(Runtime::kInterpreterNotEquals, assembler);
 }
 
 
@@ -379,7 +370,7 @@ void Interpreter::DoTestNotEqual(compiler::InterpreterAssembler* assembler) {
 //
 // Test if the value in the <src> register is strictly equal to the accumulator.
 void Interpreter::DoTestEqualStrict(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::EQ_STRICT, assembler);
+  DoBinaryOp(Runtime::kInterpreterStrictEquals, assembler);
 }
 
 
@@ -389,7 +380,7 @@ void Interpreter::DoTestEqualStrict(compiler::InterpreterAssembler* assembler) {
 // accumulator.
 void Interpreter::DoTestNotEqualStrict(
     compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::NE_STRICT, assembler);
+  DoBinaryOp(Runtime::kInterpreterStrictNotEquals, assembler);
 }
 
 
@@ -397,7 +388,7 @@ void Interpreter::DoTestNotEqualStrict(
 //
 // Test if the value in the <src> register is less than the accumulator.
 void Interpreter::DoTestLessThan(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::LT, assembler);
+  DoBinaryOp(Runtime::kInterpreterLessThan, assembler);
 }
 
 
@@ -405,36 +396,36 @@ void Interpreter::DoTestLessThan(compiler::InterpreterAssembler* assembler) {
 //
 // Test if the value in the <src> register is greater than the accumulator.
 void Interpreter::DoTestGreaterThan(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::GT, assembler);
+  DoBinaryOp(Runtime::kInterpreterGreaterThan, assembler);
 }
 
 
-// TestLessThanEqual <src>
+// TestLessThanOrEqual <src>
 //
 // Test if the value in the <src> register is less than or equal to the
 // accumulator.
-void Interpreter::DoTestLessThanEqual(
+void Interpreter::DoTestLessThanOrEqual(
     compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::LTE, assembler);
+  DoBinaryOp(Runtime::kInterpreterLessThanOrEqual, assembler);
 }
 
 
-// TestGreaterThanEqual <src>
+// TestGreaterThanOrEqual <src>
 //
 // Test if the value in the <src> register is greater than or equal to the
 // accumulator.
-void Interpreter::DoTestGreaterThanEqual(
+void Interpreter::DoTestGreaterThanOrEqual(
     compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::GTE, assembler);
+  DoBinaryOp(Runtime::kInterpreterGreaterThanOrEqual, assembler);
 }
 
 
 // TestIn <src>
 //
-// Test if the value in the <src> register is in the collection referenced
-// by the accumulator.
+// Test if the object referenced by the register operand is a property of the
+// object referenced by the accumulator.
 void Interpreter::DoTestIn(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::IN, assembler);
+  DoBinaryOp(Runtime::kHasProperty, assembler);
 }
 
 
@@ -443,7 +434,7 @@ void Interpreter::DoTestIn(compiler::InterpreterAssembler* assembler) {
 // Test if the object referenced by the <src> register is an an instance of type
 // referenced by the accumulator.
 void Interpreter::DoTestInstanceOf(compiler::InterpreterAssembler* assembler) {
-  DoCompareOp(Token::Value::INSTANCEOF, assembler);
+  DoBinaryOp(Runtime::kInstanceOf, assembler);
 }
 
 
index 217a41f..431ead9 100644 (file)
@@ -815,7 +815,7 @@ enum CompareResult {
 enum class ComparisonResult {
   kLessThan,     // x < y
   kEqual,        // x = y
-  kGreaterThan,  // x > x
+  kGreaterThan,  // x > y
   kUndefined     // at least one of x or y was undefined or NaN
 };
 
diff --git a/src/runtime/runtime-interpreter.cc b/src/runtime/runtime-interpreter.cc
new file mode 100644 (file)
index 0000000..e0a1712
--- /dev/null
@@ -0,0 +1,125 @@
+// 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.
+
+#include "src/runtime/runtime-utils.h"
+
+#include "src/arguments.h"
+#include "src/isolate-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterEquals) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::Equals(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterNotEquals) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::Equals(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(!result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterLessThan) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::LessThan(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterGreaterThan) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::GreaterThan(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterLessThanOrEqual) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::LessThanOrEqual(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterGreaterThanOrEqual) {
+  HandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
+  Maybe<bool> result = Object::GreaterThanOrEqual(x, y);
+  if (result.IsJust()) {
+    return isolate->heap()->ToBoolean(result.FromJust());
+  } else {
+    return isolate->heap()->exception();
+  }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
+  SealHandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_CHECKED(Object, x, 0);
+  CONVERT_ARG_CHECKED(Object, y, 1);
+  return isolate->heap()->ToBoolean(x->StrictEquals(y));
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
+  SealHandleScope scope(isolate);
+  DCHECK_EQ(2, args.length());
+  CONVERT_ARG_CHECKED(Object, x, 0);
+  CONVERT_ARG_CHECKED(Object, y, 1);
+  return isolate->heap()->ToBoolean(!x->StrictEquals(y));
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
+  SealHandleScope scope(isolate);
+  DCHECK_EQ(1, args.length());
+  CONVERT_ARG_CHECKED(Object, x, 0);
+  return isolate->heap()->ToBoolean(x->BooleanValue());
+}
+
+
+}  // namespace internal
+}  // namespace v8
index a3bc212..49f99bd 100644 (file)
@@ -216,6 +216,18 @@ namespace internal {
   F(ForInStep, 1, 1)
 
 
+#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
+  F(InterpreterEquals, 2, 1)              \
+  F(InterpreterNotEquals, 2, 1)           \
+  F(InterpreterStrictEquals, 2, 1)        \
+  F(InterpreterStrictNotEquals, 2, 1)     \
+  F(InterpreterLessThan, 2, 1)            \
+  F(InterpreterGreaterThan, 2, 1)         \
+  F(InterpreterLessThanOrEqual, 2, 1)     \
+  F(InterpreterGreaterThanOrEqual, 2, 1)  \
+  F(InterpreterToBoolean, 1, 1)
+
+
 #define FOR_EACH_INTRINSIC_FUNCTION(F)                      \
   F(FunctionGetName, 1, 1)                                  \
   F(FunctionSetName, 2, 1)                                  \
@@ -1058,6 +1070,7 @@ namespace internal {
   FOR_EACH_INTRINSIC_DATE(F)                \
   FOR_EACH_INTRINSIC_DEBUG(F)               \
   FOR_EACH_INTRINSIC_FORIN(F)               \
+  FOR_EACH_INTRINSIC_INTERPRETER(F)         \
   FOR_EACH_INTRINSIC_FUNCTION(F)            \
   FOR_EACH_INTRINSIC_FUTEX(F)               \
   FOR_EACH_INTRINSIC_GENERATOR(F)           \
index 8716497..e0668d5 100644 (file)
@@ -18,7 +18,7 @@ class BytecodeGeneratorHelper {
  public:
   const char* kFunctionName = "f";
 
-  const int kLastParamIndex =
+  static const int kLastParamIndex =
       -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
 
   BytecodeGeneratorHelper() {
@@ -470,7 +470,7 @@ TEST(PropertyLoads) {
        {"name"}}};
   for (size_t i = 0; i < arraysize(snippets); i++) {
     Handle<BytecodeArray> bytecode_array =
-        helper.MakeBytecode(snippets[i].code_snippet, "f");
+        helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
     CheckBytecodeArrayEqual(snippets[i], bytecode_array);
   }
 }
@@ -573,7 +573,7 @@ TEST(PropertyStores) {
        {"name"}}};
   for (size_t i = 0; i < arraysize(snippets); i++) {
     Handle<BytecodeArray> bytecode_array =
-        helper.MakeBytecode(snippets[i].code_snippet, "f");
+        helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
     CheckBytecodeArrayEqual(snippets[i], bytecode_array);
   }
 }
@@ -651,7 +651,7 @@ TEST(PropertyCall) {
        {"func"}}};
   for (size_t i = 0; i < arraysize(snippets); i++) {
     Handle<BytecodeArray> bytecode_array =
-        helper.MakeBytecode(snippets[i].code_snippet, "f");
+        helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
     CheckBytecodeArrayEqual(snippets[i], bytecode_array);
   }
 }
@@ -681,7 +681,6 @@ TEST(LoadGlobal) {
   for (size_t i = 0; i < arraysize(snippets); i++) {
     Handle<BytecodeArray> bytecode_array =
         helper.MakeBytecode(snippets[i].code_snippet, "f");
-    bytecode_array->Print();
     CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
   }
 }
@@ -737,7 +736,7 @@ TEST(IfConditions) {
   Handle<Object> unused = helper.factory()->undefined_value();
 
   ExpectedSnippet<Handle<Object>> snippets[] = {
-      {"function f() { if (0) { return 1; } else { return -1; } }",
+      {"function f() { if (0) { return 1; } else { return -1; } } f()",
        0,
        1,
        14,
@@ -753,7 +752,7 @@ TEST(IfConditions) {
         B(Return)},             //
        0,
        {unused, unused, unused, unused}},
-      {"function f() { if ('lucky') { return 1; } else { return -1; } }",
+      {"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
        0,
        1,
        15,
@@ -768,9 +767,9 @@ TEST(IfConditions) {
         B(LdaUndefined),        //
         B(Return)},             //
        1,
-       {helper.factory()->NewStringFromStaticChars("lucky"), unused,
-        unused, unused}},
-      {"function f() { if (false) { return 1; } else { return -1; } }",
+       {helper.factory()->NewStringFromStaticChars("lucky"), unused, unused,
+        unused}},
+      {"function f() { if (false) { return 1; } else { return -1; } } f();",
        0,
        1,
        13,
@@ -785,26 +784,28 @@ TEST(IfConditions) {
         B(Return)},             //
        0,
        {unused, unused, unused, unused}},
-      {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }",
+      {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }"
+       "f(99);",
        kPointerSize,
        2,
        19,
-       {B(Ldar), R(-5),              //
-        B(Star), R(0),               //
-        B(LdaZero),                  //
-        B(TestLessThanEqual), R(0),  //
-        B(JumpIfFalse), U8(7),       //
-        B(LdaConstant), U8(0),       //
-        B(Return),                   //
-        B(Jump), U8(5),              // TODO(oth): Unreachable jump after return
-        B(LdaConstant), U8(1),       //
-        B(Return),                   //
-        B(LdaUndefined),             //
-        B(Return)},                  //
+       {B(Ldar), R(-5),                //
+        B(Star), R(0),                 //
+        B(LdaZero),                    //
+        B(TestLessThanOrEqual), R(0),  //
+        B(JumpIfFalse), U8(7),         //
+        B(LdaConstant), U8(0),         //
+        B(Return),                     //
+        B(Jump), U8(5),         // TODO(oth): Unreachable jump after return
+        B(LdaConstant), U8(1),  //
+        B(Return),              //
+        B(LdaUndefined),        //
+        B(Return)},             //
        2,
        {helper.factory()->NewNumberFromInt(200),
         helper.factory()->NewNumberFromInt(-200), unused, unused}},
-      {"function f(a, b) { if (a in b) { return 200; } }",
+      {"function f(a, b) { if (a in b) { return 200; } }"
+       "f('prop', { prop: 'yes'});",
        kPointerSize,
        3,
        17,
@@ -820,27 +821,11 @@ TEST(IfConditions) {
         B(Return)},             //
        1,
        {helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
-      {"function f(a, b) { if (a instanceof b) { return 200; } }",
-       kPointerSize,
-       3,
-       17,
-       {B(Ldar), R(-6),           //
-        B(Star), R(0),            //
-        B(Ldar), R(-5),           //
-        B(TestInstanceOf), R(0),  //
-        B(JumpIfFalse), U8(7),    //
-        B(LdaConstant), U8(0),    //
-        B(Return),                //
-        B(Jump), U8(2),           // TODO(oth): Unreachable jump after return
-        B(LdaUndefined),          //
-        B(Return)},               //
-       1,
-       {helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
       {"function f(z) { var a = 0; var b = 0; if (a === 0.01) { "
 #define X "b = a; a = b; "
        X X X X X X X X X X X X X X X X X X X X X X X X
 #undef X
-       " return 200; } else { return -200; } }",
+       " return 200; } else { return -200; } } f(0.001)",
        3 * kPointerSize,
        2,
        218,
@@ -868,11 +853,51 @@ TEST(IfConditions) {
        {helper.factory()->NewHeapNumber(0.01),
         helper.factory()->NewNumberFromInt(200),
         helper.factory()->NewNumberFromInt(199),
-        helper.factory()->NewNumberFromInt(-200)}}};
+        helper.factory()->NewNumberFromInt(-200)}},
+      {"function f(a, b) {\n"
+       "  if (a == b) { return 1; }\n"
+       "  if (a === b) { return 1; }\n"
+       "  if (a < b) { return 1; }\n"
+       "  if (a > b) { return 1; }\n"
+       "  if (a <= b) { return 1; }\n"
+       "  if (a >= b) { return 1; }\n"
+       "  if (a in b) { return 1; }\n"
+       "  if (a instanceof b) { return 1; }\n"
+       "  /* if (a != b) { return 1; } */"   // TODO(oth) Ast visitor yields
+       "  /* if (a !== b) { return 1; } */"  // UNARY NOT, rather than !=/!==.
+       "  return 0;\n"
+       "} f(1, 1);",
+       kPointerSize,
+       3,
+       122,
+       {
+#define IF_CONDITION_RETURN(condition) \
+  B(Ldar), R(-6),                      \
+  B(Star), R(0),                       \
+  B(Ldar), R(-5),                      \
+  B(condition), R(0),                  \
+  B(JumpIfFalse), U8(7),               \
+  B(LdaSmi8), U8(1),                   \
+  B(Return),                           \
+  B(Jump), U8(2),
+           IF_CONDITION_RETURN(TestEqual)               //
+           IF_CONDITION_RETURN(TestEqualStrict)         //
+           IF_CONDITION_RETURN(TestLessThan)            //
+           IF_CONDITION_RETURN(TestGreaterThan)         //
+           IF_CONDITION_RETURN(TestLessThanOrEqual)     //
+           IF_CONDITION_RETURN(TestGreaterThanOrEqual)  //
+           IF_CONDITION_RETURN(TestIn)                  //
+           IF_CONDITION_RETURN(TestInstanceOf)          //
+#undef IF_CONDITION_RETURN
+           B(LdaZero),  //
+           B(Return)},  //
+       0,
+       {unused, unused, unused, unused}},
+  };
 
   for (size_t i = 0; i < arraysize(snippets); i++) {
     Handle<BytecodeArray> bytecode_array =
-        helper.MakeBytecodeForFunction(snippets[i].code_snippet);
+        helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
     CheckBytecodeArrayEqual(snippets[i], bytecode_array);
   }
 }
index adac491..81dd15e 100644 (file)
@@ -151,6 +151,7 @@ class InterpreterTester {
 
 using v8::internal::BytecodeArray;
 using v8::internal::Handle;
+using v8::internal::LanguageMode;
 using v8::internal::Object;
 using v8::internal::Runtime;
 using v8::internal::Smi;
@@ -976,3 +977,271 @@ TEST(InterpreterConditionalJumps) {
   Handle<Object> return_value = callable().ToHandleChecked();
   CHECK_EQ(Smi::cast(*return_value)->value(), 7);
 }
+
+
+static const Token::Value kComparisonTypes[] = {
+    Token::Value::EQ,        Token::Value::NE,  Token::Value::EQ_STRICT,
+    Token::Value::NE_STRICT, Token::Value::LTE, Token::Value::LTE,
+    Token::Value::GT,        Token::Value::GTE};
+
+
+template <typename T>
+bool CompareC(Token::Value op, T lhs, T rhs, bool types_differed = false) {
+  switch (op) {
+    case Token::Value::EQ:
+      return lhs == rhs;
+    case Token::Value::NE:
+      return lhs != rhs;
+    case Token::Value::EQ_STRICT:
+      return (lhs == rhs) && !types_differed;
+    case Token::Value::NE_STRICT:
+      return (lhs != rhs) || types_differed;
+    case Token::Value::LT:
+      return lhs < rhs;
+    case Token::Value::LTE:
+      return lhs <= rhs;
+    case Token::Value::GT:
+      return lhs > rhs;
+    case Token::Value::GTE:
+      return lhs >= rhs;
+    default:
+      UNREACHABLE();
+      return false;
+  }
+}
+
+
+TEST(InterpreterSmiComparisons) {
+  // NB Constants cover 31-bit space.
+  int inputs[] = {v8::internal::kMinInt / 2,
+                  v8::internal::kMinInt / 4,
+                  -108733832,
+                  -999,
+                  -42,
+                  -2,
+                  -1,
+                  0,
+                  +1,
+                  +2,
+                  42,
+                  12345678,
+                  v8::internal::kMaxInt / 4,
+                  v8::internal::kMaxInt / 2};
+
+  for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+    Token::Value comparison = kComparisonTypes[c];
+    for (size_t i = 0; i < arraysize(inputs); i++) {
+      for (size_t j = 0; j < arraysize(inputs); j++) {
+        HandleAndZoneScope handles;
+        BytecodeArrayBuilder builder(handles.main_isolate(),
+                                     handles.main_zone());
+        Register r0(0);
+        builder.set_locals_count(1);
+        builder.set_parameter_count(0);
+        builder.LoadLiteral(Smi::FromInt(inputs[i]))
+            .StoreAccumulatorInRegister(r0)
+            .LoadLiteral(Smi::FromInt(inputs[j]))
+            .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+            .Return();
+
+        Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+        InterpreterTester tester(handles.main_isolate(), bytecode_array);
+        auto callable = tester.GetCallable<>();
+        Handle<Object> return_value = callable().ToHandleChecked();
+        CHECK(return_value->IsBoolean());
+        CHECK_EQ(return_value->BooleanValue(),
+                 CompareC(comparison, inputs[i], inputs[j]));
+      }
+    }
+  }
+}
+
+
+TEST(InterpreterHeapNumberComparisons) {
+  double inputs[] = {std::numeric_limits<double>::min(),
+                     std::numeric_limits<double>::max(),
+                     -0.001,
+                     0.01,
+                     0.1000001,
+                     1e99,
+                     -1e-99};
+  for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+    Token::Value comparison = kComparisonTypes[c];
+    for (size_t i = 0; i < arraysize(inputs); i++) {
+      for (size_t j = 0; j < arraysize(inputs); j++) {
+        HandleAndZoneScope handles;
+        i::Factory* factory = handles.main_isolate()->factory();
+        BytecodeArrayBuilder builder(handles.main_isolate(),
+                                     handles.main_zone());
+        Register r0(0);
+        builder.set_locals_count(1);
+        builder.set_parameter_count(0);
+        builder.LoadLiteral(factory->NewHeapNumber(inputs[i]))
+            .StoreAccumulatorInRegister(r0)
+            .LoadLiteral(factory->NewHeapNumber(inputs[j]))
+            .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+            .Return();
+
+        Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+        InterpreterTester tester(handles.main_isolate(), bytecode_array);
+        auto callable = tester.GetCallable<>();
+        Handle<Object> return_value = callable().ToHandleChecked();
+        CHECK(return_value->IsBoolean());
+        CHECK_EQ(return_value->BooleanValue(),
+                 CompareC(comparison, inputs[i], inputs[j]));
+      }
+    }
+  }
+}
+
+
+TEST(InterpreterStringComparisons) {
+  std::string inputs[] = {"A", "abc", "z", "", "Foo!", "Foo"};
+
+  for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+    Token::Value comparison = kComparisonTypes[c];
+    for (size_t i = 0; i < arraysize(inputs); i++) {
+      for (size_t j = 0; j < arraysize(inputs); j++) {
+        const char* lhs = inputs[i].c_str();
+        const char* rhs = inputs[j].c_str();
+        HandleAndZoneScope handles;
+        i::Factory* factory = handles.main_isolate()->factory();
+        BytecodeArrayBuilder builder(handles.main_isolate(),
+                                     handles.main_zone());
+        Register r0(0);
+        builder.set_locals_count(1);
+        builder.set_parameter_count(0);
+        builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs))
+            .StoreAccumulatorInRegister(r0)
+            .LoadLiteral(factory->NewStringFromAsciiChecked(rhs))
+            .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+            .Return();
+
+        Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+        InterpreterTester tester(handles.main_isolate(), bytecode_array);
+        auto callable = tester.GetCallable<>();
+        Handle<Object> return_value = callable().ToHandleChecked();
+        CHECK(return_value->IsBoolean());
+        CHECK_EQ(return_value->BooleanValue(),
+                 CompareC(comparison, inputs[i], inputs[j]));
+      }
+    }
+  }
+}
+
+
+TEST(InterpreterMixedComparisons) {
+  // This test compares a HeapNumber with a String. The latter is
+  // convertible to a HeapNumber so comparison will be between numeric
+  // values except for the strict comparisons where no conversion is
+  // performed.
+  const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e5", "2.01"};
+
+  i::UnicodeCache unicode_cache;
+
+  for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+    Token::Value comparison = kComparisonTypes[c];
+    for (size_t i = 0; i < arraysize(inputs); i++) {
+      for (size_t j = 0; j < arraysize(inputs); j++) {
+        for (int pass = 0; pass < 2; pass++) {
+          const char* lhs_cstr = inputs[i];
+          const char* rhs_cstr = inputs[j];
+          double lhs = StringToDouble(&unicode_cache, lhs_cstr,
+                                      i::ConversionFlags::NO_FLAGS);
+          double rhs = StringToDouble(&unicode_cache, rhs_cstr,
+                                      i::ConversionFlags::NO_FLAGS);
+          HandleAndZoneScope handles;
+          i::Factory* factory = handles.main_isolate()->factory();
+          BytecodeArrayBuilder builder(handles.main_isolate(),
+                                       handles.main_zone());
+          Register r0(0);
+          builder.set_locals_count(1);
+          builder.set_parameter_count(0);
+          if (pass == 0) {
+            // Comparison with HeapNumber on the lhs and String on the rhs
+            builder.LoadLiteral(factory->NewNumber(lhs))
+                .StoreAccumulatorInRegister(r0)
+                .LoadLiteral(factory->NewStringFromAsciiChecked(rhs_cstr))
+                .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+                .Return();
+          } else {
+            // Comparison with HeapNumber on the rhs and String on the lhs
+            builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs_cstr))
+                .StoreAccumulatorInRegister(r0)
+                .LoadLiteral(factory->NewNumber(rhs))
+                .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+                .Return();
+          }
+
+          Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+          InterpreterTester tester(handles.main_isolate(), bytecode_array);
+          auto callable = tester.GetCallable<>();
+          Handle<Object> return_value = callable().ToHandleChecked();
+          CHECK(return_value->IsBoolean());
+          CHECK_EQ(return_value->BooleanValue(),
+                   CompareC(comparison, lhs, rhs, true));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(InterpreterInstanceOf) {
+  HandleAndZoneScope handles;
+  i::Factory* factory = handles.main_isolate()->factory();
+  Handle<i::String> name = factory->NewStringFromAsciiChecked("cons");
+  Handle<i::JSFunction> func = factory->NewFunction(name);
+  Handle<i::JSObject> instance = factory->NewJSObject(func);
+  Handle<i::Object> other = factory->NewNumber(3.3333);
+  Handle<i::Object> cases[] = {Handle<i::Object>::cast(instance), other};
+  for (size_t i = 0; i < arraysize(cases); i++) {
+    bool expected_value = (i == 0);
+    BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
+    Register r0(0);
+    builder.set_locals_count(1);
+    builder.set_parameter_count(0);
+    builder.LoadLiteral(cases[i]);
+    builder.StoreAccumulatorInRegister(r0)
+        .LoadLiteral(func)
+        .CompareOperation(Token::Value::INSTANCEOF, r0, LanguageMode::SLOPPY)
+        .Return();
+
+    Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+    InterpreterTester tester(handles.main_isolate(), bytecode_array);
+    auto callable = tester.GetCallable<>();
+    Handle<Object> return_value = callable().ToHandleChecked();
+    CHECK(return_value->IsBoolean());
+    CHECK_EQ(return_value->BooleanValue(), expected_value);
+  }
+}
+
+
+TEST(InterpreterTestIn) {
+  HandleAndZoneScope handles;
+  i::Factory* factory = handles.main_isolate()->factory();
+  // Allocate an array
+  Handle<i::JSArray> array =
+      factory->NewJSArray(i::ElementsKind::FAST_SMI_ELEMENTS);
+  // Check for these properties on the array object
+  const char* properties[] = {"length", "fuzzle", "x", "0"};
+  for (size_t i = 0; i < arraysize(properties); i++) {
+    bool expected_value = (i == 0);
+    BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
+    Register r0(0);
+    builder.set_locals_count(1);
+    builder.set_parameter_count(0);
+    builder.LoadLiteral(factory->NewStringFromAsciiChecked(properties[i]))
+        .StoreAccumulatorInRegister(r0)
+        .LoadLiteral(Handle<Object>::cast(array))
+        .CompareOperation(Token::Value::IN, r0, LanguageMode::SLOPPY)
+        .Return();
+
+    Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+    InterpreterTester tester(handles.main_isolate(), bytecode_array);
+    auto callable = tester.GetCallable<>();
+    Handle<Object> return_value = callable().ToHandleChecked();
+    CHECK(return_value->IsBoolean());
+    CHECK_EQ(return_value->BooleanValue(), expected_value);
+  }
+}
index 82d3b01..7299786 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
+// 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.
 
diff --git a/test/unittests/runtime/runtime-interpreter-unittest.cc b/test/unittests/runtime/runtime-interpreter-unittest.cc
new file mode 100644 (file)
index 0000000..73f29ba
--- /dev/null
@@ -0,0 +1,171 @@
+// 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.
+
+#include "src/v8.h"
+
+#include "src/factory.h"
+#include "src/heap/heap.h"
+#include "src/heap/heap-inl.h"
+#include "src/runtime/runtime.h"
+#include "test/unittests/test-utils.h"
+
+namespace v8 {
+namespace internal {
+namespace interpreter {
+
+class RuntimeInterpreterTest : public TestWithIsolateAndZone {
+ public:
+  typedef Object* (*RuntimeMethod)(int, Object**, Isolate*);
+
+  RuntimeInterpreterTest() {}
+  ~RuntimeInterpreterTest() override {}
+
+  bool TestOperatorWithObjects(RuntimeMethod method, Handle<Object> lhs,
+                               Handle<Object> rhs, bool expected);
+  bool TestOperator(RuntimeMethod method, int32_t lhs, int32_t rhs,
+                    bool expected);
+  bool TestOperator(RuntimeMethod method, double lhs, double rhs,
+                    bool expected);
+  bool TestOperator(RuntimeMethod method, const char* lhs, const char* rhs,
+                    bool expected);
+};
+
+
+bool RuntimeInterpreterTest::TestOperatorWithObjects(RuntimeMethod method,
+                                                     Handle<Object> lhs,
+                                                     Handle<Object> rhs,
+                                                     bool expected) {
+  Object* args_object[] = {*rhs, *lhs};
+  Handle<Object> result =
+      handle(method(2, &args_object[1], isolate()), isolate());
+  CHECK(result->IsTrue() || result->IsFalse());
+  return result->IsTrue() == expected;
+}
+
+
+bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, int32_t lhs,
+                                          int32_t rhs, bool expected) {
+  Handle<Object> x = isolate()->factory()->NewNumberFromInt(lhs);
+  Handle<Object> y = isolate()->factory()->NewNumberFromInt(rhs);
+  return TestOperatorWithObjects(method, x, y, expected);
+}
+
+
+bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, double lhs,
+                                          double rhs, bool expected) {
+  Handle<Object> x = isolate()->factory()->NewNumber(lhs);
+  Handle<Object> y = isolate()->factory()->NewNumber(rhs);
+  CHECK_EQ(HeapNumber::cast(*x)->value(), lhs);
+  CHECK_EQ(HeapNumber::cast(*y)->value(), rhs);
+  return TestOperatorWithObjects(method, x, y, expected);
+}
+
+
+bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, const char* lhs,
+                                          const char* rhs, bool expected) {
+  Handle<Object> x = isolate()->factory()->NewStringFromAsciiChecked(lhs);
+  Handle<Object> y = isolate()->factory()->NewStringFromAsciiChecked(rhs);
+  return TestOperatorWithObjects(method, x, y, expected);
+}
+
+
+TEST_F(RuntimeInterpreterTest, TestOperatorsWithIntegers) {
+  int32_t inputs[] = {kMinInt, Smi::kMinValue, -17,    -1, 0, 1,
+                      991,     Smi::kMaxValue, kMaxInt};
+  TRACED_FOREACH(int, lhs, inputs) {
+    TRACED_FOREACH(int, rhs, inputs) {
+#define INTEGER_OPERATOR_CHECK(r, op, x, y) \
+  CHECK(TestOperator(Runtime_Interpreter##r, x, y, x op y))
+      INTEGER_OPERATOR_CHECK(Equals, ==, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(LessThan, <, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
+      INTEGER_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
+#undef INTEGER_OPERATOR_CHECK
+    }
+  }
+}
+
+
+TEST_F(RuntimeInterpreterTest, TestOperatorsWithDoubles) {
+  double inputs[] = {std::numeric_limits<double>::min(),
+                     std::numeric_limits<double>::max(),
+                     -0.001,
+                     0.01,
+                     3.14,
+                     -6.02214086e23};
+  TRACED_FOREACH(double, lhs, inputs) {
+    TRACED_FOREACH(double, rhs, inputs) {
+#define DOUBLE_OPERATOR_CHECK(r, op, x, y) \
+  CHECK(TestOperator(Runtime_Interpreter##r, x, y, x op y))
+      DOUBLE_OPERATOR_CHECK(Equals, ==, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(LessThan, <, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
+      DOUBLE_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
+#undef DOUBLE_OPERATOR_CHECK
+    }
+  }
+}
+
+
+TEST_F(RuntimeInterpreterTest, TestOperatorsWithString) {
+  const char* inputs[] = {"abc", "a", "def", "0"};
+  TRACED_FOREACH(const char*, lhs, inputs) {
+    TRACED_FOREACH(const char*, rhs, inputs) {
+#define STRING_OPERATOR_CHECK(r, op, x, y)         \
+  CHECK(TestOperator(Runtime_Interpreter##r, x, y, \
+                     std::string(x) op std::string(y)))
+      STRING_OPERATOR_CHECK(Equals, ==, lhs, rhs);
+      STRING_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+      STRING_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+      STRING_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+      STRING_OPERATOR_CHECK(LessThan, <, lhs, rhs);
+      STRING_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
+      STRING_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
+      STRING_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
+#undef STRING_OPERATOR_CHECK
+    }
+  }
+}
+
+
+TEST_F(RuntimeInterpreterTest, ToBoolean) {
+  double quiet_nan = std::numeric_limits<double>::quiet_NaN();
+  std::pair<Handle<Object>, bool> cases[] = {
+      std::make_pair(isolate()->factory()->NewNumberFromInt(0), false),
+      std::make_pair(isolate()->factory()->NewNumberFromInt(1), true),
+      std::make_pair(isolate()->factory()->NewNumberFromInt(100), true),
+      std::make_pair(isolate()->factory()->NewNumberFromInt(-1), true),
+      std::make_pair(isolate()->factory()->NewNumber(7.7), true),
+      std::make_pair(isolate()->factory()->NewNumber(0.00001), true),
+      std::make_pair(isolate()->factory()->NewNumber(quiet_nan), false),
+      std::make_pair(isolate()->factory()->NewHeapNumber(0.0), false),
+      std::make_pair(isolate()->factory()->undefined_value(), false),
+      std::make_pair(isolate()->factory()->null_value(), false),
+      std::make_pair(isolate()->factory()->true_value(), true),
+      std::make_pair(isolate()->factory()->false_value(), false),
+      std::make_pair(isolate()->factory()->NewStringFromStaticChars(""), false),
+      std::make_pair(isolate()->factory()->NewStringFromStaticChars("_"), true),
+  };
+
+  for (size_t i = 0; i < arraysize(cases); i++) {
+    auto& value_expected_tuple = cases[i];
+    Object* args_object[] = {*value_expected_tuple.first};
+    Handle<Object> result = handle(
+        Runtime_InterpreterToBoolean(1, &args_object[0], isolate()), isolate());
+    CHECK(result->IsBoolean());
+    CHECK_EQ(result->IsTrue(), value_expected_tuple.second);
+  }
+}
+
+}  // Namespace interpreter
+}  // namespace internal
+}  // namespace v8
index 01b944b..dcd0bf5 100644 (file)
         'heap/heap-unittest.cc',
         'heap/scavenge-job-unittest.cc',
         'run-all-unittests.cc',
+        'runtime/runtime-interpreter-unittest.cc',
         'test-utils.h',
         'test-utils.cc',
       ],
index 30cdecf..c39f963 100644 (file)
         '../../src/runtime/runtime-generator.cc',
         '../../src/runtime/runtime-i18n.cc',
         '../../src/runtime/runtime-internal.cc',
+        '../../src/runtime/runtime-interpreter.cc',
         '../../src/runtime/runtime-json.cc',
         '../../src/runtime/runtime-literals.cc',
         '../../src/runtime/runtime-liveedit.cc',