289 lines
7.6 KiB
C++
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_
|