Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/test/unittests/heap/cppgc/stack-unittest.cc')
-rw-r--r--deps/v8/test/unittests/heap/cppgc/stack-unittest.cc357
1 files changed, 357 insertions, 0 deletions
diff --git a/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc b/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc
new file mode 100644
index 00000000000..7ff5274a19e
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc
@@ -0,0 +1,357 @@
+// Copyright 2020 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/heap/cppgc/stack.h"
+
+#include <memory>
+#include <ostream>
+
+#include "include/v8config.h"
+#include "src/base/platform/platform.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+#include <xmmintrin.h>
+#endif
+
+namespace cppgc {
+namespace internal {
+
+namespace {
+
+class GCStackTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ stack_.reset(new Stack(v8::base::Stack::GetStackStart()));
+ }
+
+ void TearDown() override { stack_.reset(); }
+
+ Stack* GetStack() const { return stack_.get(); }
+
+ private:
+ std::unique_ptr<Stack> stack_;
+};
+
+} // namespace
+
+TEST_F(GCStackTest, IsOnStackForStackValue) {
+ void* dummy;
+ EXPECT_TRUE(GetStack()->IsOnStack(&dummy));
+}
+
+TEST_F(GCStackTest, IsOnStackForHeapValue) {
+ auto dummy = std::make_unique<int>();
+ EXPECT_FALSE(GetStack()->IsOnStack(dummy.get()));
+}
+
+namespace {
+
+class StackScanner final : public StackVisitor {
+ public:
+ struct Container {
+ std::unique_ptr<int> value;
+ };
+
+ StackScanner() : container_(new Container{}) {
+ container_->value = std::make_unique<int>();
+ }
+
+ void VisitPointer(const void* address) final {
+ if (address == container_->value.get()) found_ = true;
+ }
+
+ void Reset() { found_ = false; }
+ bool found() const { return found_; }
+ int* needle() const { return container_->value.get(); }
+
+ private:
+ std::unique_ptr<Container> container_;
+ bool found_ = false;
+};
+
+} // namespace
+
+TEST_F(GCStackTest, IteratePointersFindsOnStackValue) {
+ auto scanner = std::make_unique<StackScanner>();
+
+ // No check that the needle is initially not found as on some platforms it
+ // may be part of temporaries after setting it up through StackScanner.
+ {
+ int* volatile tmp = scanner->needle();
+ USE(tmp);
+ GetStack()->IteratePointers(scanner.get());
+ EXPECT_TRUE(scanner->found());
+ }
+}
+
+TEST_F(GCStackTest, IteratePointersFindsOnStackValuePotentiallyUnaligned) {
+ auto scanner = std::make_unique<StackScanner>();
+
+ // No check that the needle is initially not found as on some platforms it
+ // may be part of temporaries after setting it up through StackScanner.
+ {
+ char a = 'c';
+ USE(a);
+ int* volatile tmp = scanner->needle();
+ USE(tmp);
+ GetStack()->IteratePointers(scanner.get());
+ EXPECT_TRUE(scanner->found());
+ }
+}
+
+namespace {
+
+// Prevent inlining as that would allow the compiler to prove that the parameter
+// must not actually be materialized.
+//
+// Parameter positiosn are explicit to test various calling conventions.
+V8_NOINLINE void* RecursivelyPassOnParameterImpl(void* p1, void* p2, void* p3,
+ void* p4, void* p5, void* p6,
+ void* p7, void* p8,
+ Stack* stack,
+ StackVisitor* visitor) {
+ if (p1) {
+ return RecursivelyPassOnParameterImpl(nullptr, p1, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ stack, visitor);
+ } else if (p2) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, p2, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ stack, visitor);
+ } else if (p3) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, p3,
+ nullptr, nullptr, nullptr, nullptr,
+ stack, visitor);
+ } else if (p4) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ p4, nullptr, nullptr, nullptr, stack,
+ visitor);
+ } else if (p5) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, p5, nullptr, nullptr, stack,
+ visitor);
+ } else if (p6) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, p6, nullptr, stack,
+ visitor);
+ } else if (p7) {
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, p7, stack,
+ visitor);
+ } else if (p8) {
+ stack->IteratePointers(visitor);
+ return p8;
+ }
+ return nullptr;
+}
+
+V8_NOINLINE void* RecursivelyPassOnParameter(size_t num, void* parameter,
+ Stack* stack,
+ StackVisitor* visitor) {
+ switch (num) {
+ case 0:
+ stack->IteratePointers(visitor);
+ return parameter;
+ case 1:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr,
+ parameter, stack, visitor);
+ case 2:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, parameter,
+ nullptr, stack, visitor);
+ case 3:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ nullptr, parameter, nullptr,
+ nullptr, stack, visitor);
+ case 4:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+ parameter, nullptr, nullptr,
+ nullptr, stack, visitor);
+ case 5:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr,
+ parameter, nullptr, nullptr,
+ nullptr, nullptr, stack, visitor);
+ case 6:
+ return RecursivelyPassOnParameterImpl(nullptr, nullptr, parameter,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, stack, visitor);
+ case 7:
+ return RecursivelyPassOnParameterImpl(nullptr, parameter, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, stack, visitor);
+ case 8:
+ return RecursivelyPassOnParameterImpl(parameter, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, stack, visitor);
+ default:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+}
+
+} // namespace
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting0) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(0, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting1) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(1, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting2) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(2, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting3) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(3, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting4) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(4, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting5) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(5, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting6) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(6, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting7) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(7, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(GCStackTest, IteratePointersFindsParameterNesting8) {
+ auto scanner = std::make_unique<StackScanner>();
+ void* needle = RecursivelyPassOnParameter(8, scanner->needle(), GetStack(),
+ scanner.get());
+ EXPECT_EQ(scanner->needle(), needle);
+ EXPECT_TRUE(scanner->found());
+}
+
+// The following test uses inline assembly and has been checked to work on clang
+// to verify that the stack-scanning trampoline pushes callee-saved registers.
+//
+// The test uses a macro loop as asm() can only be passed string literals.
+#ifdef __clang__
+#ifdef V8_TARGET_ARCH_X64
+#ifdef V8_OS_WIN
+
+// Excluded from test: rbp
+#define FOR_ALL_CALLEE_SAVED_REGS(V) \
+ V("rdi") \
+ V("rsi") \
+ V("rbx") \
+ V("r12") \
+ V("r13") \
+ V("r14") \
+ V("r15")
+
+#else // !V8_OS_WIN
+
+// Excluded from test: rbp
+#define FOR_ALL_CALLEE_SAVED_REGS(V) \
+ V("rbx") \
+ V("r12") \
+ V("r13") \
+ V("r14") \
+ V("r15")
+
+#endif // !V8_OS_WIN
+#endif // V8_TARGET_ARCH_X64
+#endif // __clang__
+
+#ifdef FOR_ALL_CALLEE_SAVED_REGS
+
+TEST_F(GCStackTest, IteratePointersFindsCalleeSavedRegisters) {
+ auto scanner = std::make_unique<StackScanner>();
+
+ // No check that the needle is initially not found as on some platforms it
+ // may be part of temporaries after setting it up through StackScanner.
+
+// First, clear all callee-saved registers.
+#define CLEAR_REGISTER(reg) asm("mov $0, %%" reg : : : reg);
+
+ FOR_ALL_CALLEE_SAVED_REGS(CLEAR_REGISTER)
+#undef CLEAR_REGISTER
+
+ // Keep local raw pointers to keep instruction sequences small below.
+ auto* local_stack = GetStack();
+ auto* local_scanner = scanner.get();
+
+// Moves |local_scanner->needle()| into a callee-saved register, leaving the
+// callee-saved register as the only register referencing the needle.
+// (Ignoring implementation-dependent dirty registers/stack.)
+#define KEEP_ALIVE_FROM_CALLEE_SAVED(reg) \
+ local_scanner->Reset(); \
+ /* This moves the temporary into the calee-saved register. */ \
+ asm("mov %0, %%" reg : : "r"(local_scanner->needle()) : reg); \
+ /* Register is unprotected from here till the actual invocation. */ \
+ local_stack->IteratePointers(local_scanner); \
+ EXPECT_TRUE(local_scanner->found()) \
+ << "pointer in callee-saved register not found. register: " << reg \
+ << std::endl; \
+ /* Clear out the register again */ \
+ asm("mov $0, %%" reg : : : reg);
+
+ FOR_ALL_CALLEE_SAVED_REGS(KEEP_ALIVE_FROM_CALLEE_SAVED)
+#undef KEEP_ALIVE_FROM_CALLEE_SAVED
+#undef FOR_ALL_CALLEE_SAVED_REGS
+}
+#endif // FOR_ALL_CALLEE_SAVED_REGS
+
+#if V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+class CheckStackAlignmentVisitor final : public StackVisitor {
+ public:
+ void VisitPointer(const void*) final {
+ float f[4] = {0.};
+ volatile auto xmm = ::_mm_load_ps(f);
+ USE(xmm);
+ }
+};
+
+TEST_F(GCStackTest, StackAlignment) {
+ auto checker = std::make_unique<CheckStackAlignmentVisitor>();
+ GetStack()->IteratePointers(checker.get());
+}
+#endif // V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+
+} // namespace internal
+} // namespace cppgc