blob: 1a6c8ad8608240a4db228a4c191d697a07e2d8be [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
#include "google/protobuf/port.h"
#include <stdio.h>
#include <stdlib.h>
#include <cassert>
#include <cstdint> // NOLINT
#include <gtest/gtest.h>
#include "absl/base/config.h"
// Must be included last
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
int assume_var_for_test = 1;
TEST(PortDeathTest, ProtobufAssume) {
PROTOBUF_ASSUME(assume_var_for_test == 1);
#ifdef GTEST_HAS_DEATH_TEST
#if defined(NDEBUG)
// If NDEBUG is defined, then instead of reliably crashing, the code below
// will assume a false statement. This is undefined behavior which will trip
// up sanitizers.
GTEST_SKIP() << "Can't test PROTOBUF_ASSUME()";
#else
EXPECT_DEBUG_DEATH(
PROTOBUF_ASSUME(assume_var_for_test == 2),
"port_test\\.cc:.*Assumption failed: 'assume_var_for_test == 2'");
#endif
#endif
}
TEST(PortDeathTest, UnreachableTrapsOnDebugMode) {
#ifdef GTEST_HAS_DEATH_TEST
#if defined(NDEBUG)
// In NDEBUG we crash with a UD instruction, so we don't get the "Assumption
// failed" error.
GTEST_SKIP() << "Can't test __builtin_unreachable()";
#elif ABSL_HAVE_BUILTIN(__builtin_FILE)
EXPECT_DEATH(Unreachable(),
"port_test\\.cc:.*Assumption failed: 'Unreachable'");
#else
EXPECT_DEATH(Unreachable(), "Assumption failed: 'Unreachable'");
#endif
#endif
}
#if defined(__clang__) && ABSL_HAVE_BUILTIN(__builtin_prefetch)
// This test is only intended to ensure that `Prefetch()` continues to compile
// and executes without crashing. It is difficult to programmatically verify the
// correctness of the generated prefetch instruction sequences. However,
// experiments in godbolt.org show that the generated code is correct and
// optimal: a correct and linear sequence of prefetch instructions is generated
// in the optimized modes.
// TODO: Add a benchmark to verify the `Prefetch()` effectiveness.
TEST(PortTest, PrefetchWorksWithValidOffsets) {
struct Base {
char a[256] = {1};
};
struct Derived : Base {
char b[1024] = {2};
};
Derived derived_array[3] = {};
Base* base_ptr = derived_array;
constexpr uintptr_t kOkOffset = sizeof(Derived) / 2;
constexpr uintptr_t kJustBeyondOffset = sizeof(Derived);
// A prefetch of a guaranteed valid address (using lines).
{
static constexpr PrefetchOpts kOpts = {
{1, PrefetchOpts::kLines},
{kOkOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts>(base_ptr);
}
// A prefetch of a guaranteed valid address (using bytes not wholly divisible
// into lines).
{
static constexpr PrefetchOpts kOpts = {
{sizeof(Derived) / 2, PrefetchOpts::kBytes},
{kOkOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts>(base_ptr);
}
// Stay within the unrolled for-loop body in `Prefetch()`.
{
static constexpr PrefetchOpts kOpts = {
{8, PrefetchOpts::kLines},
{kOkOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts>(base_ptr);
}
// Hit multiple iterations of the unrolled for-loop body in `Prefetch()`.
{
static constexpr PrefetchOpts kOpts = {
{100, PrefetchOpts::kLines},
{kOkOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts>(base_ptr);
}
// `base_ptr` actually points to an array of `Derived`s. Test that an explicit
// non-void pointed-to template parameter compiles and doesn't trigger the
// "type mismatch with actual pointer type" static assert.
{
static constexpr PrefetchOpts kOpts = {
{2, PrefetchOpts::kObjects},
{kOkOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts, Derived>(base_ptr);
}
// A prefetch of an invalid address (beyond the end of the buffer) is valid
// and is just a no-op.
{
static constexpr PrefetchOpts kOpts = {
{2, PrefetchOpts::kLines},
{kJustBeyondOffset, PrefetchOpts::kBytes},
};
Prefetch<kOpts>(base_ptr);
}
}
#endif // defined(__clang__) && ABSL_HAVE_BUILTIN(__builtin_prefetch)
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"