chromium/v8/test/unittests/heap/cppgc/stack-unittest.cc

// 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/base/stack.h"

#include <memory>
#include <ostream>

#include "include/v8config.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 {

Stack;
StackVisitor;

namespace {

class GCStackTest : public ::testing::Test {};

}  // namespace

#if !V8_OS_FUCHSIA
TEST_F(GCStackTest, IsOnStackForStackValue) {}
#endif  // !V8_OS_FUCHSIA

TEST_F(GCStackTest, IsOnStackForHeapValue) {}

namespace {

class StackScanner final : public StackVisitor {};

}  // namespace

TEST_F(GCStackTest, IteratePointersFindsOnStackValue) {}

TEST_F(GCStackTest, IteratePointersFindsOnStackValuePotentiallyUnaligned) {}

namespace {

// Prevent inlining as that would allow the compiler to prove that the parameter
// must not actually be materialized.
//
// Parameter positions 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) {}

V8_NOINLINE void* RecursivelyPassOnParameter(size_t num, void* parameter,
                                             Stack* stack,
                                             StackVisitor* visitor) {}

}  // namespace

TEST_F(GCStackTest, IteratePointersFindsParameterNesting0) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting1) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting2) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting3) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting4) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting5) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting6) {}

TEST_F(GCStackTest, IteratePointersFindsParameterNesting7) {}

// Disabled on msvc, due to miscompilation, see https://crbug.com/v8/10658.
#if !defined(_MSC_VER) || defined(__clang__)
TEST_F(GCStackTest, IteratePointersFindsParameterNesting8) {}
#endif  // !_MSC_VER || __clang__

namespace {
// We manually call into this function from inline assembly. Therefore we need
// to make sure that:
// 1) there is no .plt indirection (i.e. visibility is hidden);
// 2) stack is realigned in the function prologue.
extern "C" V8_NOINLINE
#if defined(__clang__)
    __attribute__((used))
#if !defined(V8_OS_WIN)
    __attribute__((visibility("hidden")))
#endif  // !defined(V8_OS_WIN)
#ifdef __has_attribute
#if __has_attribute(force_align_arg_pointer)
    __attribute__((force_align_arg_pointer))
#endif  // __has_attribute(force_align_arg_pointer)
#endif  //  __has_attribute
#endif  // defined(__clang__)
    void
    IteratePointersNoMangling(Stack* stack, StackVisitor* visitor) {}
}  // namespace

// The following tests use inline assembly and have 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

#else  // !V8_OS_WIN

// Excluded from test: rbp
#define FOR_ALL_CALLEE_SAVED_REGS

#endif  // !V8_OS_WIN
#endif  // V8_TARGET_ARCH_X64
#endif  // __clang__

#ifdef FOR_ALL_CALLEE_SAVED_REGS

TEST_F(GCStackTest, IteratePointersFindsCalleeSavedRegisters) {}
#endif  // FOR_ALL_CALLEE_SAVED_REGS

#if defined(__clang__) && defined(V8_TARGET_ARCH_X64) && defined(V8_OS_WIN)

#define FOR_ALL_XMM_CALLEE_SAVED_REGS

TEST_F(GCStackTest, IteratePointersFindsCalleeSavedXMMRegisters) {
  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 xmm registers.
#define CLEAR_REGISTER

  FOR_ALL_XMM_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

  // First, test the pointer in the low quadword.
#define MOVE_TO_REG_AND_CALL

  FOR_ALL_XMM_CALLEE_SAVED_REGS(KEEP_ALIVE_FROM_CALLEE_SAVED)

#undef MOVE_TO_REG_AND_CALL
  // Then, test the pointer in the upper quadword.
#define MOVE_TO_REG_AND_CALL

  FOR_ALL_XMM_CALLEE_SAVED_REGS(KEEP_ALIVE_FROM_CALLEE_SAVED)
#undef MOVE_TO_REG_AND_CALL
#undef KEEP_ALIVE_FROM_CALLEE_SAVED
#undef FOR_ALL_XMM_CALLEE_SAVED_REGS
}

#endif  // defined(__clang__) && defined(V8_TARGET_ARCH_X64) &&
        // defined(V8_OS_WIN)

#if V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
class CheckStackAlignmentVisitor final : public StackVisitor {};

TEST_F(GCStackTest, StackAlignment) {}
#endif  // V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)

}  // namespace internal
}  // namespace cppgc