From 12e8b34b0747ea402a91bf46e3810c6da339f478 Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Fri, 8 Aug 2014 07:04:07 +0000 Subject: [PATCH] Reland "Add initial support for compiler unit tests using GTest/GMock.". 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 --- build/all.gyp | 1 + src/compiler/code-generator-impl.h | 2 +- src/compiler/instruction-selector-impl.h | 2 +- test/base-unittests/DEPS | 2 +- test/compiler-unittests/DEPS | 5 + .../arm/instruction-selector-arm-unittest.cc | 27 ++++ test/compiler-unittests/compiler-unittests.cc | 86 +++++++++++++ test/compiler-unittests/compiler-unittests.gyp | 58 +++++++++ test/compiler-unittests/compiler-unittests.h | 60 +++++++++ test/compiler-unittests/compiler-unittests.status | 6 + .../instruction-selector-unittest.cc | 92 ++++++++++++++ .../instruction-selector-unittest.h | 137 +++++++++++++++++++++ test/compiler-unittests/testcfg.py | 51 ++++++++ tools/presubmit.py | 3 +- tools/run-tests.py | 2 +- 15 files changed, 529 insertions(+), 5 deletions(-) create mode 100644 test/compiler-unittests/DEPS create mode 100644 test/compiler-unittests/arm/instruction-selector-arm-unittest.cc create mode 100644 test/compiler-unittests/compiler-unittests.cc create mode 100644 test/compiler-unittests/compiler-unittests.gyp create mode 100644 test/compiler-unittests/compiler-unittests.h create mode 100644 test/compiler-unittests/compiler-unittests.status create mode 100644 test/compiler-unittests/instruction-selector-unittest.cc create mode 100644 test/compiler-unittests/instruction-selector-unittest.h create mode 100644 test/compiler-unittests/testcfg.py diff --git a/build/all.gyp b/build/all.gyp index b0b4fa6..5e410a3 100644 --- a/build/all.gyp +++ b/build/all.gyp @@ -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"', { diff --git a/src/compiler/code-generator-impl.h b/src/compiler/code-generator-impl.h index cdab800..a3f7e4c 100644 --- a/src/compiler/code-generator-impl.h +++ b/src/compiler/code-generator-impl.h @@ -65,7 +65,7 @@ class InstructionOperandConverter { } BasicBlock* InputBlock(int index) { - NodeId block_id = static_cast(instr_->InputAt(index)->index()); + NodeId block_id = static_cast(InputInt32(index)); // operand should be a block id. DCHECK(block_id >= 0); DCHECK(block_id < gen_->schedule()->BasicBlockCount()); diff --git a/src/compiler/instruction-selector-impl.h b/src/compiler/instruction-selector-impl.h index 573c1ff..ac446b3 100644 --- a/src/compiler/instruction-selector-impl.h +++ b/src/compiler/instruction-selector-impl.h @@ -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: diff --git a/test/base-unittests/DEPS b/test/base-unittests/DEPS index 80f6711..90b0800 100644 --- a/test/base-unittests/DEPS +++ b/test/base-unittests/DEPS @@ -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 index 0000000..cf66c5a --- /dev/null +++ b/test/compiler-unittests/DEPS @@ -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 index 0000000..3e51722 --- /dev/null +++ b/test/compiler-unittests/arm/instruction-selector-arm-unittest.cc @@ -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 index 0000000..2ce4c93 --- /dev/null +++ b/test/compiler-unittests/compiler-unittests.cc @@ -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 index 0000000..e7048de --- /dev/null +++ b/test/compiler-unittests/compiler-unittests.gyp @@ -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 index 0000000..ad8e99c --- /dev/null +++ b/test/compiler-unittests/compiler-unittests.h @@ -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_); } + 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 index 0000000..d439913 --- /dev/null +++ b/test/compiler-unittests/compiler-unittests.status @@ -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 index 0000000..a862f98 --- /dev/null +++ b/test/compiler-unittests/instruction-selector-unittest.cc @@ -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 index 0000000..f13b2d7 --- /dev/null +++ b/test/compiler-unittests/instruction-selector-unittest.h @@ -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 + +#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(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(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 ConstantMap; + + ConstantMap constants_; + ConstantMap immediates_; + std::deque 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 index 0000000..4eec956 --- /dev/null +++ b/test/compiler-unittests/testcfg.py @@ -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) diff --git a/tools/presubmit.py b/tools/presubmit.py index bb4293e..aba5005 100755 --- a/tools/presubmit.py +++ b/tools/presubmit.py @@ -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): diff --git a/tools/run-tests.py b/tools/run-tests.py index 415b66e..6e9f554 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -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 } -- 2.7.4