(root)/
gcc-13.2.0/
libstdc++-v3/
testsuite/
experimental/
simd/
tests/
bits/
test_values.h
       1  // Copyright (C) 2020-2023 Free Software Foundation, Inc.
       2  //
       3  // This file is part of the GNU ISO C++ Library.  This library is free
       4  // software; you can redistribute it and/or modify it under the
       5  // terms of the GNU General Public License as published by the
       6  // Free Software Foundation; either version 3, or (at your option)
       7  // any later version.
       8  //
       9  // This library is distributed in the hope that it will be useful,
      10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12  // GNU General Public License for more details.
      13  //
      14  // You should have received a copy of the GNU General Public License along
      15  // with this library; see the file COPYING3.  If not see
      16  // <http://www.gnu.org/licenses/>.
      17  
      18  #ifndef SIMD_TESTS_BITS_TEST_VALUES_H_
      19  #define SIMD_TESTS_BITS_TEST_VALUES_H_
      20  
      21  #include "verify.h"
      22  
      23  #include <experimental/simd>
      24  #include <initializer_list>
      25  #include <random>
      26  #include <cfenv>
      27  
      28  template <class T, class A>
      29    std::experimental::simd<T, A>
      30    iif(std::experimental::simd_mask<T, A> k,
      31        const typename std::experimental::simd_mask<T, A>::simd_type& t,
      32        const std::experimental::simd<T, A>& f)
      33    {
      34      auto r = f;
      35      where(k, r) = t;
      36      return r;
      37    }
      38  
      39  template <class V>
      40    V
      41    epilogue_load(const typename V::value_type* mem, const std::size_t size)
      42    {
      43      const int rem = size % V::size();
      44      return where(V([](int i) { return i; }) < rem, V(0))
      45  	     .copy_from(mem + size / V::size() * V::size(),
      46  			std::experimental::element_aligned);
      47    }
      48  
      49  template <class V, class... F>
      50    void
      51    test_values(const std::initializer_list<typename V::value_type>& inputs,
      52  	      F&&... fun_pack)
      53    {
      54      for (auto it = inputs.begin(); it + V::size() <= inputs.end();
      55  	 it += V::size())
      56        {
      57  	[](auto...) {
      58  	}((fun_pack(V(&it[0], std::experimental::element_aligned)), 0)...);
      59        }
      60      [](auto...) {
      61      }((fun_pack(epilogue_load<V>(inputs.begin(), inputs.size())), 0)...);
      62    }
      63  
      64  template <class V>
      65    struct RandomValues
      66    {
      67      using T = typename V::value_type;
      68      static constexpr bool isfp = std::is_floating_point_v<T>;
      69      const std::size_t count;
      70  
      71      std::conditional_t<std::is_floating_point_v<T>,
      72  		       std::uniform_real_distribution<T>,
      73  		       std::uniform_int_distribution<T>>
      74        dist;
      75  
      76      const bool uniform;
      77  
      78      const T abs_max = std::__finite_max_v<T>;
      79  
      80      RandomValues(std::size_t count_, T min, T max)
      81      : count(count_), dist(min, max), uniform(true)
      82      {
      83        if constexpr (std::is_floating_point_v<T>)
      84  	VERIFY(max - min <= std::__finite_max_v<T>);
      85      }
      86  
      87      RandomValues(std::size_t count_)
      88      : count(count_), dist(isfp ? 1 : std::__finite_min_v<T>,
      89  			  isfp ? 2 : std::__finite_max_v<T>),
      90        uniform(!isfp)
      91      {}
      92  
      93      RandomValues(std::size_t count_, T abs_max_)
      94      : count(count_), dist(isfp ? 1 : -abs_max_, isfp ? 2 : abs_max_),
      95        uniform(!isfp), abs_max(abs_max_)
      96      {}
      97  
      98      template <typename URBG>
      99        V
     100        operator()(URBG& gen)
     101        {
     102  	if constexpr (!isfp)
     103  	  return V([&](int) { return dist(gen); });
     104  	else if (uniform)
     105  	  return V([&](int) { return dist(gen); });
     106  	else
     107  	  {
     108  	    auto exp_dist
     109  	      = std::normal_distribution<float>(0.f,
     110  						std::__max_exponent_v<T> * .5f);
     111  	    return V([&](int) {
     112  		     const T mant = dist(gen);
     113  		     T fp = 0;
     114  		     do {
     115  		       const int exp = exp_dist(gen);
     116  		       fp = std::ldexp(mant, exp);
     117  		     } while (fp >= abs_max || fp <= std::__denorm_min_v<T>);
     118  		     fp = gen() & 0x4 ? fp : -fp;
     119  		     return fp;
     120  		   });
     121  	  }
     122        }
     123    };
     124  
     125  static std::mt19937 g_mt_gen{0};
     126  
     127  template <class V, class... F>
     128    void
     129    test_values(const std::initializer_list<typename V::value_type>& inputs,
     130  	      RandomValues<V> random, F&&... fun_pack)
     131    {
     132      test_values<V>(inputs, fun_pack...);
     133      for (size_t i = 0; i < (random.count + V::size() - 1) / V::size(); ++i)
     134        {
     135  	[](auto...) {}((fun_pack(random(g_mt_gen)), 0)...);
     136        }
     137    }
     138  
     139  template <class V, class... F>
     140    void
     141    test_values_2arg(const std::initializer_list<typename V::value_type>& inputs,
     142  		   F&&... fun_pack)
     143    {
     144      for (auto scalar_it = inputs.begin(); scalar_it != inputs.end();
     145  	 ++scalar_it)
     146        {
     147  	for (auto it = inputs.begin(); it + V::size() <= inputs.end();
     148  	     it += V::size())
     149  	  {
     150  	    [](auto...) {
     151  	    }((fun_pack(V(&it[0], std::experimental::element_aligned),
     152  			V(*scalar_it)),
     153  	       0)...);
     154  	  }
     155  	[](auto...) {
     156  	}((fun_pack(epilogue_load<V>(inputs.begin(), inputs.size()),
     157  		    V(*scalar_it)),
     158  	   0)...);
     159        }
     160    }
     161  
     162  template <class V, class... F>
     163    void
     164    test_values_2arg(const std::initializer_list<typename V::value_type>& inputs,
     165  		   RandomValues<V> random, F&&... fun_pack)
     166    {
     167      test_values_2arg<V>(inputs, fun_pack...);
     168      for (size_t i = 0; i < (random.count + V::size() - 1) / V::size(); ++i)
     169        {
     170  	[](auto...) {}((fun_pack(random(g_mt_gen), random(g_mt_gen)), 0)...);
     171        }
     172    }
     173  
     174  template <class V, class... F>
     175    void
     176    test_values_3arg(const std::initializer_list<typename V::value_type>& inputs,
     177  		   F&&... fun_pack)
     178    {
     179      for (auto scalar_it1 = inputs.begin(); scalar_it1 != inputs.end();
     180  	 ++scalar_it1)
     181        {
     182  	for (auto scalar_it2 = inputs.begin(); scalar_it2 != inputs.end();
     183  	     ++scalar_it2)
     184  	  {
     185  	    for (auto it = inputs.begin(); it + V::size() <= inputs.end();
     186  		 it += V::size())
     187  	      {
     188  		[](auto...) {
     189  		}((fun_pack(V(&it[0], std::experimental::element_aligned),
     190  			    V(*scalar_it1), V(*scalar_it2)),
     191  		   0)...);
     192  	      }
     193  	    [](auto...) {
     194  	    }((fun_pack(epilogue_load<V>(inputs.begin(), inputs.size()),
     195  			V(*scalar_it1), V(*scalar_it2)),
     196  	       0)...);
     197  	  }
     198        }
     199    }
     200  
     201  template <class V, class... F>
     202    void
     203    test_values_3arg(const std::initializer_list<typename V::value_type>& inputs,
     204  		   RandomValues<V> random, F&&... fun_pack)
     205    {
     206      test_values_3arg<V>(inputs, fun_pack...);
     207      for (size_t i = 0; i < (random.count + V::size() - 1) / V::size(); ++i)
     208        {
     209  	[](auto...) {
     210  	}((fun_pack(random(g_mt_gen), random(g_mt_gen), random(g_mt_gen)),
     211  	   0)...);
     212        }
     213    }
     214  
     215  #if __GCC_IEC_559 < 2
     216  // Without IEC559 we consider -0, subnormals, +/-inf, and all NaNs to be
     217  // invalid (potential UB when used or "produced"). This can't use isnormal (or
     218  // any other classification function), since they know about the UB.
     219  template <class V>
     220    typename V::mask_type
     221    isvalid(V x)
     222    {
     223      using namespace std::experimental::parallelism_v2;
     224      using namespace std::experimental::parallelism_v2::__proposed;
     225      using T = typename V::value_type;
     226      if constexpr (sizeof(T) <= sizeof(double))
     227        {
     228  	using I = rebind_simd_t<__int_for_sizeof_t<T>, V>;
     229  	const I abs_x = simd_bit_cast<I>(abs(x));
     230  	const I min = simd_bit_cast<I>(V(std::__norm_min_v<T>));
     231  	const I max = simd_bit_cast<I>(V(std::__finite_max_v<T>));
     232  	return static_simd_cast<typename V::mask_type>(
     233  		 simd_bit_cast<I>(x) == 0 || (abs_x >= min && abs_x <= max));
     234        }
     235      else
     236        {
     237  	const V abs_x = abs(x);
     238  	const V min = std::__norm_min_v<T>;
     239  	// Make max non-const static to inhibit constprop. Otherwise the
     240  	// compiler might decide `abs_x <= max` is constexpr true, by definition
     241  	// (-ffinite-math-only)
     242  	static V max = std::__finite_max_v<T>;
     243  	return (x == 0 && copysign(V(1), x) == V(1))
     244  		 || (abs_x >= min && abs_x <= max);
     245        }
     246    }
     247  
     248  #define MAKE_TESTER_2(name_, reference_)                                       \
     249    [&](auto... inputs) {                                                        \
     250      ((where(!isvalid(inputs), inputs) = 1), ...);                              \
     251      const auto totest = name_(inputs...);                                      \
     252      using R = std::remove_const_t<decltype(totest)>;                           \
     253      auto&& expected = [&](const auto&... vs) -> const R {                      \
     254        R tmp = {};                                                              \
     255        for (std::size_t i = 0; i < R::size(); ++i)                              \
     256  	tmp[i] = reference_(vs[i]...);                                         \
     257        return tmp;                                                              \
     258      };                                                                         \
     259      const R expect1 = expected(inputs...);                                     \
     260      if constexpr (std::is_floating_point_v<typename R::value_type>)            \
     261        {                                                                        \
     262  	((where(!isvalid(expect1), inputs) = 1), ...);                         \
     263  	const R expect2 = expected(inputs...);                                 \
     264  	((FUZZY_COMPARE(name_(inputs...), expect2) << "\ninputs = ")           \
     265  	 << ... << inputs);                                                    \
     266        }                                                                        \
     267      else                                                                       \
     268        ((COMPARE(name_(inputs...), expect1) << "\n" #name_ "(")                 \
     269         << ... << inputs)                                                       \
     270  	<< ")";                                                                \
     271    }
     272  
     273  #define MAKE_TESTER_NOFPEXCEPT(name_)                                          \
     274    [&](auto... inputs) {                                                        \
     275      ((where(!isvalid(inputs), inputs) = 1), ...);                              \
     276      using R = std::remove_const_t<decltype(name_(inputs...))>;                 \
     277      auto&& expected = [&](const auto&... vs) -> const R {                      \
     278        R tmp = {};                                                              \
     279        for (std::size_t i = 0; i < R::size(); ++i)                              \
     280  	tmp[i] = std::name_(vs[i]...);                                         \
     281        return tmp;                                                              \
     282      };                                                                         \
     283      const R expect1 = expected(inputs...);                                     \
     284      if constexpr (std::is_floating_point_v<typename R::value_type>)            \
     285        {                                                                        \
     286  	((where(!isvalid(expect1), inputs) = 1), ...);                         \
     287  	std::feclearexcept(FE_ALL_EXCEPT);                                     \
     288  	asm volatile("");                                                      \
     289  	auto totest = name_(inputs...);                                        \
     290  	asm volatile("");                                                      \
     291  	((COMPARE(std::fetestexcept(FE_ALL_EXCEPT), 0) << "\n" #name_ "(")     \
     292  	 << ... << inputs)                                                     \
     293  	  << ")";                                                              \
     294  	const R expect2 = expected(inputs...);                                 \
     295  	std::feclearexcept(FE_ALL_EXCEPT);                                     \
     296  	asm volatile("");                                                      \
     297  	totest = name_(inputs...);                                             \
     298  	asm volatile("");                                                      \
     299  	((COMPARE(std::fetestexcept(FE_ALL_EXCEPT), 0) << "\n" #name_ "(")     \
     300  	 << ... << inputs)                                                     \
     301  	  << ")";                                                              \
     302  	((FUZZY_COMPARE(totest, expect2) << "\n" #name_ "(") << ... << inputs) \
     303  	  << ")";                                                              \
     304        }                                                                        \
     305      else                                                                       \
     306        {                                                                        \
     307  	std::feclearexcept(FE_ALL_EXCEPT);                                     \
     308  	asm volatile("");                                                      \
     309  	auto totest = name_(inputs...);                                        \
     310  	asm volatile("");                                                      \
     311  	((COMPARE(std::fetestexcept(FE_ALL_EXCEPT), 0) << "\n" #name_ "(")     \
     312  	 << ... << inputs)                                                     \
     313  	  << ")";                                                              \
     314  	((COMPARE(totest, expect1) << "\n" #name_ "(") << ... << inputs)       \
     315  	  << ")";                                                              \
     316        }                                                                        \
     317    }
     318  
     319  #else
     320  
     321  #define MAKE_TESTER_2(name_, reference_)                                       \
     322    [&](auto... inputs) {                                                        \
     323      const auto totest = name_(inputs...);                                      \
     324      using R = std::remove_const_t<decltype(totest)>;                           \
     325      auto&& expected = [&](const auto&... vs) -> const R {                      \
     326        R tmp = {};                                                              \
     327        for (std::size_t i = 0; i < R::size(); ++i)                              \
     328  	tmp[i] = reference_(vs[i]...);                                         \
     329        return tmp;                                                              \
     330      };                                                                         \
     331      const R expect1 = expected(inputs...);                                     \
     332      if constexpr (std::is_floating_point_v<typename R::value_type>)            \
     333        {                                                                        \
     334  	((COMPARE(isnan(totest), isnan(expect1)) << #name_ "(")                \
     335  	 << ... << inputs)                                                     \
     336  	  << ") = " << totest << " != " << expect1;                            \
     337  	((where(isnan(expect1), inputs) = 0), ...);                            \
     338  	((FUZZY_COMPARE(name_(inputs...), expected(inputs...))                 \
     339  	  << "\nclean = ")                                                     \
     340  	 << ... << inputs);                                                    \
     341        }                                                                        \
     342      else                                                                       \
     343        ((COMPARE(name_(inputs...), expect1) << "\n" #name_ "(")                 \
     344         << ... << inputs)                                                       \
     345  	<< ")";                                                                \
     346    }
     347  
     348  #define MAKE_TESTER_NOFPEXCEPT(name_)                                          \
     349    [&](auto... inputs) {                                                        \
     350      std::feclearexcept(FE_ALL_EXCEPT);                                         \
     351      auto totest = name_(inputs...);                                            \
     352      ((COMPARE(std::fetestexcept(FE_ALL_EXCEPT), 0) << "\n" #name_ "(")         \
     353       << ... << inputs)                                                         \
     354        << ")";                                                                  \
     355      using R = std::remove_const_t<decltype(totest)>;                           \
     356      auto&& expected = [&](const auto&... vs) -> const R {                      \
     357        R tmp = {};                                                              \
     358        for (std::size_t i = 0; i < R::size(); ++i)                              \
     359  	tmp[i] = std::name_(vs[i]...);                                         \
     360        return tmp;                                                              \
     361      };                                                                         \
     362      const R expect1 = expected(inputs...);                                     \
     363      if constexpr (std::is_floating_point_v<typename R::value_type>)            \
     364        {                                                                        \
     365  	((COMPARE(isnan(totest), isnan(expect1)) << #name_ "(")                \
     366  	 << ... << inputs)                                                     \
     367  	  << ") = " << totest << " != " << expect1;                            \
     368  	((where(isnan(expect1), inputs) = 0), ...);                            \
     369  	const R expect2 = expected(inputs...);                                 \
     370  	std::feclearexcept(FE_ALL_EXCEPT);                                     \
     371  	asm volatile("");                                                      \
     372  	totest = name_(inputs...);                                             \
     373  	asm volatile("");                                                      \
     374  	((COMPARE(std::fetestexcept(FE_ALL_EXCEPT), 0) << "\n" #name_ "(")     \
     375  	 << ... << inputs)                                                     \
     376  	  << ")";                                                              \
     377  	FUZZY_COMPARE(totest, expect2);                                        \
     378        }                                                                        \
     379      else                                                                       \
     380        {                                                                        \
     381  	((COMPARE(totest, expect1) << "\n" #name_ "(") << ... << inputs)       \
     382  	  << ")";                                                              \
     383        }                                                                        \
     384    }
     385  
     386  #endif
     387  
     388  #define MAKE_TESTER(name_) MAKE_TESTER_2(name_, std::name_)
     389  #endif  // SIMD_TESTS_BITS_TEST_VALUES_H_