gcc/libstdc++-v3/testsuite/experimental/simd/tests/bits/verify.h
2024-01-03 12:19:35 +01:00

289 lines
7.6 KiB
C++

// Copyright (C) 2020-2024 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#ifndef TESTS_BITS_VERIFY_H_
#define TESTS_BITS_VERIFY_H_
#include <experimental/simd>
#include <sstream>
#include <iomanip>
#include "ulp.h"
#ifdef _GLIBCXX_SIMD_HAVE_NEON
// work around PR89357:
#define alignas(...) __attribute__((aligned(__VA_ARGS__)))
#endif
using schar = signed char;
using uchar = unsigned char;
using ushort = unsigned short;
using uint = unsigned int;
using ulong = unsigned long;
using llong = long long;
using ullong = unsigned long long;
using ldouble = long double;
using wchar = wchar_t;
using char16 = char16_t;
using char32 = char32_t;
template <class T>
T
make_value_unknown(const T& x)
{
if constexpr (std::is_constructible_v<T, const volatile T&>)
{
const volatile T& y = x;
return y;
}
else
{
T y = x;
asm("" : "+m"(y));
return y;
}
}
class verify
{
const bool m_failed = false;
size_t m_ip = 0;
template <typename T,
typename = decltype(std::declval<std::stringstream&>()
<< std::declval<const T&>())>
void
print(const T& x, int) const
{
std::stringstream ss;
ss << x;
__builtin_fprintf(stderr, "%s", ss.str().c_str());
}
template <typename T>
void
print(const T& x, ...) const
{
if constexpr (std::experimental::is_simd_v<T>)
{
std::stringstream ss;
if constexpr (std::is_floating_point_v<typename T::value_type>)
{
ss << '(' << x[0] << " == " << std::hexfloat << x[0]
<< std::defaultfloat << ')';
for (unsigned i = 1; i < x.size(); ++i)
{
ss << (i % 4 == 0 ? ",\n(" : ", (") << x[i]
<< " == " << std::hexfloat << x[i] << std::defaultfloat
<< ')';
}
}
else
{
ss << +x[0];
for (unsigned i = 1; i < x.size(); ++i)
{
ss << ", " << +x[i];
}
}
__builtin_fprintf(stderr, "%s", ss.str().c_str());
}
else if constexpr (std::experimental::is_simd_mask_v<T>)
{
__builtin_fprintf(stderr, (x[0] ? "[1" : "[0"));
for (unsigned i = 1; i < x.size(); ++i)
{
__builtin_fprintf(stderr, (x[i] ? "1" : "0"));
}
__builtin_fprintf(stderr, "]");
}
else
{
print_hex(&x, sizeof(T));
}
}
void
print_hex(const void* x, std::size_t n) const
{
__builtin_fprintf(stderr, "0x");
const auto* bytes = static_cast<const unsigned char*>(x);
for (std::size_t i = 0; i < n; ++i)
{
__builtin_fprintf(stderr, (i && i % 4 == 0) ? "'%02x" : "%02x",
bytes[i]);
}
}
public:
template <typename... Ts>
[[gnu::always_inline]]
verify(bool ok, const char* file, const int line,
const char* func, const char* cond, const Ts&... extra_info)
: m_failed(!ok), m_ip(get_ip())
{
if (m_failed)
[&] {
__builtin_fprintf(stderr, "%s:%d: (%s):\nInstruction Pointer: %zx\n"
"Assertion '%s' failed.\n",
file, line, func, m_ip, cond);
(print(extra_info, int()), ...);
}();
}
[[gnu::always_inline]] ~verify()
{
if (m_failed)
{
__builtin_fprintf(stderr, "\n");
__builtin_abort();
}
}
template <typename T>
[[gnu::always_inline]]
const verify&
operator<<(const T& x) const
{
if (m_failed)
print(x, int());
return *this;
}
template <typename... Ts>
[[gnu::always_inline]]
const verify&
on_failure(const Ts&... xs) const
{
if (m_failed)
[&] { (print(xs, int()), ...); }();
return *this;
}
[[gnu::always_inline]] static inline
size_t
get_ip()
{
size_t _ip = 0;
#ifdef __x86_64__
asm volatile("lea 0(%%rip),%0" : "=r"(_ip));
#elif defined __i386__
asm volatile("1: movl $1b,%0" : "=r"(_ip));
#elif defined __arm__
asm volatile("mov %0,pc" : "=r"(_ip));
#elif defined __aarch64__
asm volatile("adr %0,." : "=r"(_ip));
#endif
return _ip;
}
};
#if __FLT_EVAL_METHOD__ != 0
template <typename T>
[[gnu::always_inline]] inline decltype(auto)
force_fp_truncation(const T& x)
{
namespace stdx = std::experimental;
if constexpr (stdx::is_simd_v<T>)
{
using U = typename T::value_type;
if constexpr (std::is_floating_point_v<typename T::value_type>
&& sizeof(U) <= 8 && (sizeof(T) < 16 || std::is_same_v<
T, stdx::fixed_size_simd<U, T::size()>>))
{
T y = x;
asm("" : "+m"(y));
return y;
}
else
return x;
}
else if constexpr (std::is_floating_point_v<T> && sizeof(T) <= 8)
{
T y = x;
asm("" : "+m"(y));
return y;
}
else
return x;
}
#define COMPARE(_a, _b) \
[&](auto&& _aa, auto&& _bb) { \
return verify(std::experimental::all_of(_aa == _bb), __FILE__, __LINE__, \
__PRETTY_FUNCTION__, #_a " == " #_b, #_a " = ", _aa, \
"\n" #_b " = ", _bb); \
}(force_fp_truncation(_a), force_fp_truncation(_b))
#else
#define COMPARE(_a, _b) \
[&](auto&& _aa, auto&& _bb) { \
return verify(std::experimental::all_of(_aa == _bb), __FILE__, __LINE__, \
__PRETTY_FUNCTION__, #_a " == " #_b, #_a " = ", _aa, \
"\n" #_b " = ", _bb); \
}((_a), (_b))
#endif
#define VERIFY(_test) \
verify(_test, __FILE__, __LINE__, __PRETTY_FUNCTION__, #_test)
// ulp_distance_signed can raise FP exceptions and thus must be conditionally
// executed
#define ULP_COMPARE(_a, _b, _allowed_distance) \
[&](auto&& _aa, auto&& _bb) { \
const bool success = std::experimental::all_of( \
vir::test::ulp_distance(_aa, _bb) <= (_allowed_distance)); \
return verify(success, __FILE__, __LINE__, __PRETTY_FUNCTION__, \
#_a " ~~ " #_b, #_a " = ", _aa, "\n" #_b " = ", _bb, \
"\ndistance = ", \
success ? 0 : vir::test::ulp_distance_signed(_aa, _bb)); \
}((_a), (_b))
namespace vir {
namespace test
{
template <typename T>
inline T _S_fuzzyness = 0;
template <typename T>
void
setFuzzyness(T x)
{ _S_fuzzyness<T> = x; }
} // namespace test
} // namespace vir
#define FUZZY_COMPARE(_a, _b) \
ULP_COMPARE( \
_a, _b, \
vir::test::_S_fuzzyness<vir::test::value_type_t<decltype((_a) + (_b))>>)
template <typename V>
void
test();
template <typename V>
void
invoke_test(...)
{}
template <typename V, typename = decltype(V())>
void
invoke_test(int)
{
test<V>();
__builtin_fprintf(stderr, "PASS: %s\n", __PRETTY_FUNCTION__);
}
#endif // TESTS_BITS_VERIFY_H_