Reland "Add initial support for compiler unit tests using GTest/GMock.".
authorbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 8 Aug 2014 07:04:07 +0000 (07:04 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 8 Aug 2014 07:04:07 +0000 (07:04 +0000)
LOG=y
BUG=v8:3489
R=svenpanne@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22987 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

15 files changed:
build/all.gyp
src/compiler/code-generator-impl.h
src/compiler/instruction-selector-impl.h
test/base-unittests/DEPS
test/compiler-unittests/DEPS [new file with mode: 0644]
test/compiler-unittests/arm/instruction-selector-arm-unittest.cc [new file with mode: 0644]
test/compiler-unittests/compiler-unittests.cc [new file with mode: 0644]
test/compiler-unittests/compiler-unittests.gyp [new file with mode: 0644]
test/compiler-unittests/compiler-unittests.h [new file with mode: 0644]
test/compiler-unittests/compiler-unittests.status [new file with mode: 0644]
test/compiler-unittests/instruction-selector-unittest.cc [new file with mode: 0644]
test/compiler-unittests/instruction-selector-unittest.h [new file with mode: 0644]
test/compiler-unittests/testcfg.py [new file with mode: 0644]
tools/presubmit.py
tools/run-tests.py

index b0b4fa6..5e410a3 100644 (file)
@@ -12,6 +12,7 @@
         '../src/d8.gyp:d8',
         '../test/base-unittests/base-unittests.gyp:*',
         '../test/cctest/cctest.gyp:*',
+        '../test/compiler-unittests/compiler-unittests.gyp:*',
       ],
       'conditions': [
         ['component!="shared_library"', {
index cdab800..a3f7e4c 100644 (file)
@@ -65,7 +65,7 @@ class InstructionOperandConverter {
   }
 
   BasicBlock* InputBlock(int index) {
-    NodeId block_id = static_cast<NodeId>(instr_->InputAt(index)->index());
+    NodeId block_id = static_cast<NodeId>(InputInt32(index));
     // operand should be a block id.
     DCHECK(block_id >= 0);
     DCHECK(block_id < gen_->schedule()->BasicBlockCount());
index 573c1ff..ac446b3 100644 (file)
@@ -145,7 +145,7 @@ class OperandGenerator {
 
   InstructionOperand* Label(BasicBlock* block) {
     // TODO(bmeurer): We misuse ImmediateOperand here.
-    return ImmediateOperand::Create(block->id(), zone());
+    return TempImmediate(block->id());
   }
 
  protected:
index 80f6711..90b0800 100644 (file)
@@ -4,5 +4,5 @@ include_rules = [
   "+include/v8stdint.h",
   "-src",
   "+src/base",
-  "+testing",
+  "+testing/gtest",
 ]
diff --git a/test/compiler-unittests/DEPS b/test/compiler-unittests/DEPS
new file mode 100644 (file)
index 0000000..cf66c5a
--- /dev/null
@@ -0,0 +1,5 @@
+include_rules = [
+  "+src",
+  "+testing/gmock",
+  "+testing/gtest",
+]
diff --git a/test/compiler-unittests/arm/instruction-selector-arm-unittest.cc b/test/compiler-unittests/arm/instruction-selector-arm-unittest.cc
new file mode 100644 (file)
index 0000000..3e51722
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2014 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 "test/compiler-unittests/instruction-selector-unittest.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class InstructionSelectorARMTest : public InstructionSelectorTest {};
+
+
+COMPILER_TEST_F(InstructionSelectorARMTest, Int32AddP) {
+  StreamBuilder m(this, kMachineWord32, kMachineWord32, kMachineWord32);
+  m.Return(m.Int32Add(m.Parameter(0), m.Parameter(1)));
+  Stream s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kArmAdd, s[0]->arch_opcode());
+  EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
+  EXPECT_EQ(2U, s[0]->InputCount());
+  EXPECT_EQ(1U, s[0]->OutputCount());
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/test/compiler-unittests/compiler-unittests.cc b/test/compiler-unittests/compiler-unittests.cc
new file mode 100644 (file)
index 0000000..2ce4c93
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright 2014 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 "include/libplatform/libplatform.h"
+#include "test/compiler-unittests/compiler-unittests.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::IsNull;
+using testing::NotNull;
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// static
+v8::Isolate* CompilerTest::isolate_ = NULL;
+
+
+CompilerTest::CompilerTest()
+    : isolate_scope_(isolate_),
+      handle_scope_(isolate_),
+      context_scope_(v8::Context::New(isolate_)),
+      zone_(isolate()) {}
+
+
+CompilerTest::~CompilerTest() {}
+
+
+// static
+void CompilerTest::SetUpTestCase() {
+  Test::SetUpTestCase();
+  EXPECT_THAT(isolate_, IsNull());
+  isolate_ = v8::Isolate::New();
+  ASSERT_THAT(isolate_, NotNull());
+}
+
+
+// static
+void CompilerTest::TearDownTestCase() {
+  ASSERT_THAT(isolate_, NotNull());
+  isolate_->Dispose();
+  isolate_ = NULL;
+  Test::TearDownTestCase();
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+
+namespace {
+
+class CompilerTestEnvironment V8_FINAL : public ::testing::Environment {
+ public:
+  CompilerTestEnvironment() : platform_(NULL) {}
+  ~CompilerTestEnvironment() {}
+
+  virtual void SetUp() V8_OVERRIDE {
+    EXPECT_THAT(platform_, IsNull());
+    platform_ = v8::platform::CreateDefaultPlatform();
+    v8::V8::InitializePlatform(platform_);
+    ASSERT_TRUE(v8::V8::Initialize());
+  }
+
+  virtual void TearDown() V8_OVERRIDE {
+    ASSERT_THAT(platform_, NotNull());
+    v8::V8::Dispose();
+    v8::V8::ShutdownPlatform();
+    delete platform_;
+    platform_ = NULL;
+  }
+
+ private:
+  v8::Platform* platform_;
+};
+
+}
+
+
+int main(int argc, char** argv) {
+  testing::InitGoogleMock(&argc, argv);
+  testing::AddGlobalTestEnvironment(new CompilerTestEnvironment);
+  v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/compiler-unittests/compiler-unittests.gyp b/test/compiler-unittests/compiler-unittests.gyp
new file mode 100644 (file)
index 0000000..e7048de
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright 2014 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.
+
+{
+  'variables': {
+    'v8_code': 1,
+  },
+  'includes': ['../../build/toolchain.gypi', '../../build/features.gypi'],
+  'targets': [
+    {
+      'target_name': 'compiler-unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../../testing/gmock.gyp:gmock',
+        '../../testing/gtest.gyp:gtest',
+        '../../tools/gyp/v8.gyp:v8_libplatform',
+      ],
+      'include_dirs': [
+        '../..',
+      ],
+      'sources': [  ### gcmole(all) ###
+        'compiler-unittests.cc',
+        'instruction-selector-unittest.cc',
+      ],
+      'conditions': [
+        ['v8_target_arch=="arm"', {
+          'sources': [  ### gcmole(arch:arm) ###
+            'arm/instruction-selector-arm-unittest.cc',
+          ],
+        }],
+        ['component=="shared_library"', {
+          # compiler-unittests can't be built against a shared library, so we
+          # need to depend on the underlying static target in that case.
+          'conditions': [
+            ['v8_use_snapshot=="true"', {
+              'dependencies': ['../../tools/gyp/v8.gyp:v8_snapshot'],
+            },
+            {
+              'dependencies': [
+                '../../tools/gyp/v8.gyp:v8_nosnapshot',
+              ],
+            }],
+          ],
+        }, {
+          'dependencies': ['../../tools/gyp/v8.gyp:v8'],
+        }],
+        ['os_posix == 1', {
+          # TODO(svenpanne): This is a temporary work-around to fix the warnings
+          # that show up because we use -std=gnu++0x instead of -std=c++11.
+          'cflags!': [
+            '-pedantic',
+          ],
+        }],
+      ],
+    },
+  ],
+}
diff --git a/test/compiler-unittests/compiler-unittests.h b/test/compiler-unittests/compiler-unittests.h
new file mode 100644 (file)
index 0000000..ad8e99c
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2014 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.
+
+#ifndef V8_COMPILER_UNITTESTS_COMPILER_UNITTESTS_H_
+#define V8_COMPILER_UNITTESTS_COMPILER_UNITTESTS_H_
+
+#include "include/v8.h"
+#include "src/zone.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// The COMPILER_TEST(Case, Name) macro works just like
+// TEST(Case, Name), except that the test is disabled
+// if the platform is not a supported TurboFan target.
+#if V8_TURBOFAN_TARGET
+#define COMPILER_TEST(Case, Name) TEST(Case, Name)
+#else
+#define COMPILER_TEST(Case, Name) TEST(Case, DISABLED_##Name)
+#endif
+
+
+// The COMPILER_TEST_F(Case, Name) macro works just like
+// TEST_F(Case, Name), except that the test is disabled
+// if the platform is not a supported TurboFan target.
+#if V8_TURBOFAN_TARGET
+#define COMPILER_TEST_F(Case, Name) TEST_F(Case, Name)
+#else
+#define COMPILER_TEST_F(Case, Name) TEST_F(Case, DISABLED_##Name)
+#endif
+
+
+class CompilerTest : public ::testing::Test {
+ public:
+  CompilerTest();
+  virtual ~CompilerTest();
+
+  Isolate* isolate() const { return reinterpret_cast<Isolate*>(isolate_); }
+  Zone* zone() { return &zone_; }
+
+ protected:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+
+ private:
+  static v8::Isolate* isolate_;
+  v8::Isolate::Scope isolate_scope_;
+  v8::HandleScope handle_scope_;
+  v8::Context::Scope context_scope_;
+  Zone zone_;
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_UNITTESTS_COMPILER_UNITTESTS_H_
diff --git a/test/compiler-unittests/compiler-unittests.status b/test/compiler-unittests/compiler-unittests.status
new file mode 100644 (file)
index 0000000..d439913
--- /dev/null
@@ -0,0 +1,6 @@
+# Copyright 2014 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/compiler-unittests/instruction-selector-unittest.cc b/test/compiler-unittests/instruction-selector-unittest.cc
new file mode 100644 (file)
index 0000000..a862f98
--- /dev/null
@@ -0,0 +1,92 @@
+// Copyright 2014 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 "test/compiler-unittests/instruction-selector-unittest.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
+    InstructionSelector::Features features,
+    InstructionSelectorTest::StreamBuilderMode mode) {
+  Schedule* schedule = Export();
+  EXPECT_NE(0, graph()->NodeCount());
+  CompilationInfo info(test_->isolate(), test_->zone());
+  Linkage linkage(&info, call_descriptor());
+  InstructionSequence sequence(&linkage, graph(), schedule);
+  SourcePositionTable source_position_table(graph());
+  InstructionSelector selector(&sequence, &source_position_table, features);
+  selector.SelectInstructions();
+  if (FLAG_trace_turbo) {
+    OFStream out(stdout);
+    out << "--- Code sequence after instruction selection ---" << endl
+        << sequence;
+  }
+  Stream s;
+  for (InstructionSequence::const_iterator i = sequence.begin();
+       i != sequence.end(); ++i) {
+    Instruction* instr = *i;
+    if (instr->opcode() < 0) continue;
+    if (mode == kTargetInstructions) {
+      switch (instr->arch_opcode()) {
+#define CASE(Name) \
+  case k##Name:    \
+    break;
+        TARGET_ARCH_OPCODE_LIST(CASE)
+#undef CASE
+        default:
+          continue;
+      }
+    }
+    for (size_t i = 0; i < instr->OutputCount(); ++i) {
+      InstructionOperand* output = instr->OutputAt(i);
+      EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
+      if (output->IsConstant()) {
+        s.constants_.insert(std::make_pair(
+            output->index(), sequence.GetConstant(output->index())));
+      }
+    }
+    for (size_t i = 0; i < instr->InputCount(); ++i) {
+      InstructionOperand* input = instr->InputAt(i);
+      EXPECT_NE(InstructionOperand::CONSTANT, input->kind());
+      if (input->IsImmediate()) {
+        s.immediates_.insert(std::make_pair(
+            input->index(), sequence.GetImmediate(input->index())));
+      }
+    }
+    s.instructions_.push_back(instr);
+  }
+  return s;
+}
+
+
+COMPILER_TEST_F(InstructionSelectorTest, ReturnP) {
+  StreamBuilder m(this, kMachineWord32, kMachineWord32);
+  m.Return(m.Parameter(0));
+  Stream s = m.Build(kAllInstructions);
+  ASSERT_EQ(2U, s.size());
+  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(kArchRet, s[1]->arch_opcode());
+  EXPECT_EQ(1U, s[1]->InputCount());
+}
+
+
+COMPILER_TEST_F(InstructionSelectorTest, ReturnImm) {
+  StreamBuilder m(this, kMachineWord32);
+  m.Return(m.Int32Constant(0));
+  Stream s = m.Build(kAllInstructions);
+  ASSERT_EQ(2U, s.size());
+  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(InstructionOperand::CONSTANT, s[0]->OutputAt(0)->kind());
+  EXPECT_EQ(0, s.ToInt32(s[0]->OutputAt(0)));
+  EXPECT_EQ(kArchRet, s[1]->arch_opcode());
+  EXPECT_EQ(1U, s[1]->InputCount());
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/test/compiler-unittests/instruction-selector-unittest.h b/test/compiler-unittests/instruction-selector-unittest.h
new file mode 100644 (file)
index 0000000..f13b2d7
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright 2014 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.
+
+#ifndef V8_COMPILER_UNITTESTS_INSTRUCTION_SELECTOR_UNITTEST_H_
+#define V8_COMPILER_UNITTESTS_INSTRUCTION_SELECTOR_UNITTEST_H_
+
+#include <deque>
+
+#include "src/compiler/instruction-selector.h"
+#include "src/compiler/raw-machine-assembler.h"
+#include "test/compiler-unittests/compiler-unittests.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class InstructionSelectorTest : public CompilerTest {
+ public:
+  InstructionSelectorTest() {}
+  virtual ~InstructionSelectorTest() {}
+
+ protected:
+  class Stream;
+
+  enum StreamBuilderMode { kAllInstructions, kTargetInstructions };
+
+  class StreamBuilder V8_FINAL : public RawMachineAssembler {
+   public:
+    StreamBuilder(InstructionSelectorTest* test,
+                  MachineRepresentation return_type)
+        : RawMachineAssembler(new (test->zone()) Graph(test->zone()),
+                              CallDescriptorBuilder(test->zone(), return_type)),
+          test_(test) {}
+    StreamBuilder(InstructionSelectorTest* test,
+                  MachineRepresentation return_type,
+                  MachineRepresentation parameter0_type)
+        : RawMachineAssembler(new (test->zone()) Graph(test->zone()),
+                              CallDescriptorBuilder(test->zone(), return_type,
+                                                    parameter0_type)),
+          test_(test) {}
+    StreamBuilder(InstructionSelectorTest* test,
+                  MachineRepresentation return_type,
+                  MachineRepresentation parameter0_type,
+                  MachineRepresentation parameter1_type)
+        : RawMachineAssembler(
+              new (test->zone()) Graph(test->zone()),
+              CallDescriptorBuilder(test->zone(), return_type, parameter0_type,
+                                    parameter1_type)),
+          test_(test) {}
+
+    Stream Build(CpuFeature feature) {
+      return Build(InstructionSelector::Features(feature));
+    }
+    Stream Build(CpuFeature feature1, CpuFeature feature2) {
+      return Build(InstructionSelector::Features(feature1, feature2));
+    }
+    Stream Build(StreamBuilderMode mode = kTargetInstructions) {
+      return Build(InstructionSelector::Features(), mode);
+    }
+    Stream Build(InstructionSelector::Features features,
+                 StreamBuilderMode mode = kTargetInstructions);
+
+   private:
+    MachineCallDescriptorBuilder* CallDescriptorBuilder(
+        Zone* zone, MachineRepresentation return_type) {
+      return new (zone) MachineCallDescriptorBuilder(return_type, 0, NULL);
+    }
+
+    MachineCallDescriptorBuilder* CallDescriptorBuilder(
+        Zone* zone, MachineRepresentation return_type,
+        MachineRepresentation parameter0_type) {
+      MachineRepresentation* parameter_types =
+          zone->NewArray<MachineRepresentation>(1);
+      parameter_types[0] = parameter0_type;
+      return new (zone)
+          MachineCallDescriptorBuilder(return_type, 1, parameter_types);
+    }
+
+    MachineCallDescriptorBuilder* CallDescriptorBuilder(
+        Zone* zone, MachineRepresentation return_type,
+        MachineRepresentation parameter0_type,
+        MachineRepresentation parameter1_type) {
+      MachineRepresentation* parameter_types =
+          zone->NewArray<MachineRepresentation>(2);
+      parameter_types[0] = parameter0_type;
+      parameter_types[1] = parameter1_type;
+      return new (zone)
+          MachineCallDescriptorBuilder(return_type, 2, parameter_types);
+    }
+
+   private:
+    InstructionSelectorTest* test_;
+  };
+
+  class Stream V8_FINAL {
+   public:
+    size_t size() const { return instructions_.size(); }
+    const Instruction* operator[](size_t index) const {
+      EXPECT_LT(index, size());
+      return instructions_[index];
+    }
+
+    int32_t ToInt32(const InstructionOperand* operand) const {
+      return ToConstant(operand).ToInt32();
+    }
+
+   private:
+    Constant ToConstant(const InstructionOperand* operand) const {
+      ConstantMap::const_iterator i;
+      if (operand->IsConstant()) {
+        i = constants_.find(operand->index());
+        EXPECT_NE(constants_.end(), i);
+      } else {
+        EXPECT_EQ(InstructionOperand::IMMEDIATE, operand->kind());
+        i = immediates_.find(operand->index());
+        EXPECT_NE(immediates_.end(), i);
+      }
+      EXPECT_EQ(operand->index(), i->first);
+      return i->second;
+    }
+
+    friend class StreamBuilder;
+
+    typedef std::map<int, Constant> ConstantMap;
+
+    ConstantMap constants_;
+    ConstantMap immediates_;
+    std::deque<Instruction*> instructions_;
+  };
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_UNITTESTS_INSTRUCTION_SELECTOR_UNITTEST_H_
diff --git a/test/compiler-unittests/testcfg.py b/test/compiler-unittests/testcfg.py
new file mode 100644 (file)
index 0000000..4eec956
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright 2014 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.
+
+import os
+import shutil
+
+from testrunner.local import commands
+from testrunner.local import testsuite
+from testrunner.local import utils
+from testrunner.objects import testcase
+
+
+class CompilerUnitTestsSuite(testsuite.TestSuite):
+  def __init__(self, name, root):
+    super(CompilerUnitTestsSuite, self).__init__(name, root)
+
+  def ListTests(self, context):
+    shell = os.path.abspath(os.path.join(context.shell_dir, self.shell()))
+    if utils.IsWindows():
+      shell += ".exe"
+    output = commands.Execute(context.command_prefix +
+                              [shell, "--gtest_list_tests"] +
+                              context.extra_flags)
+    if output.exit_code != 0:
+      print output.stdout
+      print output.stderr
+      return []
+    tests = []
+    test_case = ''
+    for test_desc in output.stdout.strip().split():
+      if test_desc.endswith('.'):
+        test_case = test_desc
+      else:
+        test = testcase.TestCase(self, test_case + test_desc, dependency=None)
+        tests.append(test)
+    tests.sort()
+    return tests
+
+  def GetFlagsForTestCase(self, testcase, context):
+    return (testcase.flags + ["--gtest_filter=" + testcase.path] +
+            ["--gtest_random_seed=%s" % context.random_seed] +
+            ["--gtest_print_time=0"] +
+            context.mode_flags)
+
+  def shell(self):
+    return "compiler-unittests"
+
+
+def GetSuite(name, root):
+  return CompilerUnitTestsSuite(name, root)
index bb4293e..aba5005 100755 (executable)
@@ -237,7 +237,8 @@ class CppLintProcessor(SourceFileProcessor):
   def GetPathsToSearch(self):
     return ['src', 'include', 'samples',
             join('test', 'base-unittests'),
-            join('test', 'cctest')]
+            join('test', 'cctest'),
+            join('test', 'compiler-unittests')]
 
   def GetCpplintScript(self, prio_path):
     for path in [prio_path] + os.environ["PATH"].split(os.pathsep):
index 415b66e..6e9f554 100755 (executable)
@@ -51,7 +51,7 @@ from testrunner.objects import context
 
 ARCH_GUESS = utils.DefaultArch()
 DEFAULT_TESTS = ["mjsunit", "fuzz-natives", "base-unittests",
-                 "cctest", "message", "preparser"]
+                 "cctest", "compiler-unittests", "message", "preparser"]
 TIMEOUT_DEFAULT = 60
 TIMEOUT_SCALEFACTOR = {"debug"   : 4,
                        "release" : 1 }