mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
libstdc++: Implement P4012R1 while reverting P3844R2 (consteval simd broadcast)
P3844R2 added consteval conversion for value-preserving conversion from constants. It had been approved by LEWG in Kona. Therefore, the current implementation has the consteval broadcast constructor. In Croydon, LEWG reversed the decision but changed the overload set to keep the design space open for C++29. This patch implements the removal of the consteval constructor and changes the broadcast constructor according to P4012R1, to keep the design space open. libstdc++-v3/ChangeLog: * include/bits/simd_details.h (__value_preserving_cast): Remove. * include/bits/simd_mask.h (basic_mask): Replace plain 0 and 1 literals with cw<0> and cw<1>. Replace explicit basic_vec construction from 0 and 1 with default init and broadcast from _Up(1). (_M_to_uint): Replace 1 with cw<1>. * include/bits/simd_vec.h (basic_vec): Remove consteval broadcast overload. Remove explicit broadcast from non-value-preserving types. * testsuite/std/simd/arithmetic.cc: Replace ill-formed integer literals with explicit cast to T or use cw. * testsuite/std/simd/mask.cc: Likewise. * testsuite/std/simd/simd_alg.cc: Likewise. * testsuite/std/simd/traits_common.cc: Adjust for resulting traits changes. * testsuite/std/simd/traits_math.cc: Likewise. Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
This commit is contained in:
@@ -1241,15 +1241,6 @@ namespace simd
|
||||
return static_cast<_To>(__x);
|
||||
}
|
||||
|
||||
template <typename _From, typename _To>
|
||||
concept __simd_vec_bcast_consteval
|
||||
= __explicitly_convertible_to<_From, _To>
|
||||
&& is_arithmetic_v<remove_cvref_t<_From>> && convertible_to<_From, _To>
|
||||
&& !__value_preserving_convertible_to<remove_cvref_t<_From>, _To>
|
||||
&& (is_same_v<common_type_t<_From, _To>, _To>
|
||||
|| (is_same_v<remove_cvref_t<_From>, int> && is_integral_v<_To>)
|
||||
|| (is_same_v<remove_cvref_t<_From>, unsigned> && unsigned_integral<_To>));
|
||||
|
||||
/** @internal
|
||||
* std::pair is not trivially copyable, this one is
|
||||
*/
|
||||
|
||||
@@ -865,11 +865,12 @@ namespace simd
|
||||
using _Ip = typename _VecType::value_type;
|
||||
_VecType __v0 = _Ip(__val);
|
||||
constexpr int __bits_per_element = sizeof(_Ip) * __CHAR_BIT__;
|
||||
constexpr _VecType __pow2 = _VecType(1) << (__iota<_VecType> % __bits_per_element);
|
||||
constexpr _VecType __pow2 = _VecType(cw<1>)
|
||||
<< (__iota<_VecType> % cw<__bits_per_element>);
|
||||
if constexpr (_S_size < __bits_per_element)
|
||||
return ((__v0 & __pow2) > 0)._M_concat_data();
|
||||
return ((__v0 & __pow2) > cw<0>)._M_concat_data();
|
||||
else if constexpr (_S_size == __bits_per_element)
|
||||
return ((__v0 & __pow2) != 0)._M_concat_data();
|
||||
return ((__v0 & __pow2) != cw<0>)._M_concat_data();
|
||||
else
|
||||
{
|
||||
static_assert(_Bytes == 1);
|
||||
@@ -886,7 +887,7 @@ namespace simd
|
||||
};
|
||||
__v1 *= 0x0101'0101'0101'0101ull;
|
||||
__v0 = __builtin_bit_cast(_VecType, __v1);
|
||||
return ((__v0 & __pow2) != 0)._M_data;
|
||||
return ((__v0 & __pow2) != cw<0>)._M_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -895,7 +896,7 @@ namespace simd
|
||||
__v0 = _VecType::_S_static_permute(__v1, [](int __i) {
|
||||
return __i / __CHAR_BIT__;
|
||||
});
|
||||
return ((__v0 & __pow2) != 0)._M_data;
|
||||
return ((__v0 & __pow2) != cw<0>)._M_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -991,7 +992,7 @@ namespace simd
|
||||
else
|
||||
{
|
||||
using _UV = basic_vec<_Up, _UAbi>;
|
||||
return __select_impl(static_cast<_UV::mask_type>(*this), _UV(1), _UV(0));
|
||||
return __select_impl(static_cast<_UV::mask_type>(*this), _Up(1), _UV());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,7 +1067,7 @@ namespace simd
|
||||
constexpr int __n = _IV::size();
|
||||
if constexpr (_Bytes * __CHAR_BIT__ >= __n) // '1 << __iota' cannot overflow
|
||||
{ // reduce(select(k, powers_of_2, 0))
|
||||
constexpr _IV __pow2 = _IV(1) << __iota<_IV>;
|
||||
constexpr _IV __pow2 = _IV(cw<1>) << __iota<_IV>;
|
||||
return _Ur(_U0(__select_impl(__k, __pow2, _IV())
|
||||
._M_reduce(bit_or<>()))) << _Offset;
|
||||
}
|
||||
@@ -1079,7 +1080,7 @@ namespace simd
|
||||
}
|
||||
else
|
||||
{ // limit powers_of_2 to 1, 2, 4, ..., 128
|
||||
constexpr _IV __pow2 = _IV(1) << (__iota<_IV> % _IV(__CHAR_BIT__));
|
||||
constexpr _IV __pow2 = _IV(cw<1>) << (__iota<_IV> % _IV(cw<__CHAR_BIT__>));
|
||||
_IV __x = __select_impl(__k, __pow2, _IV());
|
||||
// partial reductions of 8 neighboring elements
|
||||
__x |= _IV::_S_static_permute(__x, _SwapNeighbors<4>());
|
||||
|
||||
@@ -1135,20 +1135,13 @@ namespace simd
|
||||
*
|
||||
* @note The constructor is implicit if the conversion (if any) is value-preserving.
|
||||
*/
|
||||
template <__explicitly_convertible_to<value_type> _Up>
|
||||
template <__broadcast_constructible<value_type> _Up>
|
||||
[[__gnu__::__always_inline__]]
|
||||
constexpr explicit(!__broadcast_constructible<_Up, value_type>)
|
||||
constexpr
|
||||
basic_vec(_Up&& __x) noexcept
|
||||
: _M_data(_DataType() == _DataType() ? static_cast<value_type>(__x) : value_type())
|
||||
{}
|
||||
|
||||
template <__simd_vec_bcast_consteval<value_type> _Up>
|
||||
consteval
|
||||
basic_vec(_Up&& __x)
|
||||
: _M_data(_DataType() == _DataType()
|
||||
? __value_preserving_cast<value_type>(__x) : value_type())
|
||||
{}
|
||||
|
||||
// [simd.ctor] conversion constructor -----------------------------------
|
||||
template <typename _Up, typename _UAbi, _TargetTraits _Traits = {}>
|
||||
requires (_S_size == _UAbi::_S_size)
|
||||
@@ -2037,20 +2030,13 @@ namespace simd
|
||||
{ return _M_concat_data(); }
|
||||
|
||||
// [simd.ctor] broadcast constructor ------------------------------------
|
||||
template <__explicitly_convertible_to<value_type> _Up>
|
||||
template <__broadcast_constructible<value_type> _Up>
|
||||
[[__gnu__::__always_inline__]]
|
||||
constexpr explicit(!__broadcast_constructible<_Up, value_type>)
|
||||
constexpr
|
||||
basic_vec(_Up&& __x) noexcept
|
||||
: _M_data0(static_cast<value_type>(__x)), _M_data1(static_cast<value_type>(__x))
|
||||
{}
|
||||
|
||||
template <__simd_vec_bcast_consteval<value_type> _Up>
|
||||
consteval
|
||||
basic_vec(_Up&& __x)
|
||||
: _M_data0(__value_preserving_cast<value_type>(__x)),
|
||||
_M_data1(__value_preserving_cast<value_type>(__x))
|
||||
{}
|
||||
|
||||
// [simd.ctor] conversion constructor -----------------------------------
|
||||
template <typename _Up, typename _UAbi>
|
||||
requires (_S_size == _UAbi::_S_size)
|
||||
|
||||
@@ -88,7 +88,8 @@ template <typename V>
|
||||
};
|
||||
|
||||
ADD_TEST(multiplication) {
|
||||
std::tuple {V(), V(RealV(1), RealV()), V(RealV(), RealV(1)), init_vec<V, C(0, 2), C(2, 0), C(-1, 2)>},
|
||||
std::tuple {V(), V(RealV(Real(1)), RealV()), V(RealV(), RealV(Real(1))),
|
||||
init_vec<V, C(0, 2), C(2, 0), C(-1, 2)>},
|
||||
[](auto& t, V x, V one, V I, V z) {
|
||||
t.verify_equal(x * x, x);
|
||||
t.verify_equal(x * z, x);
|
||||
@@ -181,11 +182,11 @@ template <typename V>
|
||||
t.verify_equal(y, x - T(1));
|
||||
t.verify_equal(x - x, y);
|
||||
t.verify_equal(x = z - x, init_vec<V, 0, 1, 2, 3, 4, 5, 6>);
|
||||
t.verify_equal(x = z - x, V(1));
|
||||
t.verify_equal(x = z - x, T(1));
|
||||
t.verify_equal(z -= x, init_vec<V, 0, 1, 2, 3, 4, 5, 6>);
|
||||
t.verify_equal(z, init_vec<V, 0, 1, 2, 3, 4, 5, 6>);
|
||||
t.verify_equal(z -= z, V(0));
|
||||
t.verify_equal(z, V(0));
|
||||
t.verify_equal(z -= z, V());
|
||||
t.verify_equal(z, V());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -259,10 +260,10 @@ template <typename V>
|
||||
ADD_TEST(divide0, std::is_floating_point_v<T> && !is_iec559) {
|
||||
std::tuple{T(2), init_vec<V, 1, 2, 3, 4, 5, 6, 7>},
|
||||
[](auto& t, V x, V y) {
|
||||
t.verify_equal_to_ulp(x / x, V(T(1)), 1);
|
||||
t.verify_equal_to_ulp(T(3) / x, V(T(3) / T(2)), 1);
|
||||
t.verify_equal_to_ulp(x / T(3), V(T(2) / T(3)), 1);
|
||||
t.verify_equal_to_ulp(y / x, init_vec<V, .5, 1, 1.5, 2, 2.5, 3, 3.5>, 1);
|
||||
t.verify_equal_to_ulp(x / x, V(T(1)), std::cw<1>);
|
||||
t.verify_equal_to_ulp(T(3) / x, V(T(3) / T(2)), std::cw<1>);
|
||||
t.verify_equal_to_ulp(x / T(3), V(T(2) / T(3)), std::cw<1>);
|
||||
t.verify_equal_to_ulp(y / x, init_vec<V, .5, 1, 1.5, 2, 2.5, 3, 3.5>, std::cw<1>);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -272,18 +273,18 @@ template <typename V>
|
||||
[](auto& t, V a) {
|
||||
V b = std::cw<2>;
|
||||
V ref([&](int i) { return a[i] / 2; });
|
||||
t.verify_equal_to_ulp(a / b, ref, 1);
|
||||
t.verify_equal_to_ulp(a / b, ref, std::cw<1>);
|
||||
a = select(a == std::cw<0>, T(1), a);
|
||||
// -freciprocal-math together with flush-to-zero makes
|
||||
// the following range restriction necessary (i.e.
|
||||
// 1/|a| must be >= min). Intel vrcpps and vrcp14ps
|
||||
// need some extra slack (use 1.1 instead of 1).
|
||||
a = select(fabs(a) >= T(1.1) / norm_min, T(1), a);
|
||||
t.verify_equal_to_ulp(a / a, V(1), 1)("\na = ", a);
|
||||
t.verify_equal_to_ulp(a / a, V(std::cw<1>), std::cw<1>)("\na = ", a);
|
||||
ref = V([&](int i) { return 2 / a[i]; });
|
||||
t.verify_equal_to_ulp(b / a, ref, 1)("\na = ", a);
|
||||
t.verify_equal_to_ulp(b /= a, ref, 1);
|
||||
t.verify_equal_to_ulp(b, ref, 1);
|
||||
t.verify_equal_to_ulp(b / a, ref, std::cw<1>)("\na = ", a);
|
||||
t.verify_equal_to_ulp(b /= a, ref, std::cw<1>);
|
||||
t.verify_equal_to_ulp(b, ref, std::cw<1>);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -291,15 +292,15 @@ template <typename V>
|
||||
std::tuple{T(2), init_vec<V, 1, 2, 3, 4, 5, 6, 7>, init_vec<V, T(max), T(norm_min)>,
|
||||
init_vec<V, T(norm_min), T(max)>, init_vec<V, T(max), T(norm_min) + 1>},
|
||||
[](auto& t, V x, V y, V z, V a, V b) {
|
||||
t.verify_equal(x / x, V(1));
|
||||
t.verify_equal(T(3) / x, V(T(3) / T(2)));
|
||||
t.verify_equal(x / T(3), V(T(2) / T(3)));
|
||||
t.verify_equal(x / x, T(1));
|
||||
t.verify_equal(T(3) / x, T(T(3) / T(2)));
|
||||
t.verify_equal(x / T(3), T(T(2) / T(3)));
|
||||
t.verify_equal(y / x, init_vec<V, .5, 1, 1.5, 2, 2.5, 3, 3.5>);
|
||||
V ref = init_vec<V, T(max / 2), T(norm_min / 2)>;
|
||||
t.verify_equal(z / x, ref);
|
||||
ref = init_vec<V, T(norm_min / 2), T(max / 2)>;
|
||||
t.verify_equal(a / x, ref);
|
||||
t.verify_equal(b / b, V(1));
|
||||
t.verify_equal(b / b, T(1));
|
||||
ref = init_vec<V, T(2 / max), T(2 / (norm_min + 1))>;
|
||||
t.verify_equal(x / b, ref);
|
||||
t.verify_equal(x /= b, ref);
|
||||
|
||||
@@ -54,14 +54,14 @@ template <typename V>
|
||||
return i % 13 == 0 || i % 7 == 0;
|
||||
})},
|
||||
[](auto& t, const M k, const M tr, const M fa, const M k2) {
|
||||
t.verify_equal(V(+tr), V(1));
|
||||
t.verify_equal(V(+tr), T(1));
|
||||
t.verify_equal(V(+fa), V());
|
||||
t.verify_equal(V(+k), init_vec<V, 0, 1>);
|
||||
|
||||
if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
t.verify_equal(V(~tr), ~V(1));
|
||||
t.verify_equal(V(~fa), ~V(0));
|
||||
t.verify_equal(V(~tr), ~V(std::cw<1>));
|
||||
t.verify_equal(V(~fa), ~V());
|
||||
t.verify_equal(V(~k), ~init_vec<V, 0, 1>);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ template <typename V>
|
||||
using M = typename V::mask_type;
|
||||
|
||||
using pair = std::pair<V, V>;
|
||||
static constexpr std::conditional_t<std::is_floating_point_v<T>, short, T> x_max
|
||||
= test_iota_max<V, 1>;
|
||||
static constexpr T x_max = test_iota_max<V, 1>;
|
||||
static constexpr int x_max_int = static_cast<int>(x_max);
|
||||
|
||||
static constexpr V
|
||||
@@ -26,7 +25,7 @@ template <typename V>
|
||||
return static_cast<V>(std::to_underlying(x_max) - static_cast<Vu>(x));
|
||||
}
|
||||
else
|
||||
return x_max - x;
|
||||
return std::cw<x_max> - x;
|
||||
}
|
||||
|
||||
ADD_TEST(Select) {
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace test02
|
||||
// ensure 'true ? int : vec<float>' doesn't work
|
||||
template <typename T>
|
||||
concept has_type_member = requires { typename T::type; };
|
||||
static_assert(has_type_member<common_type<int, simd::vec<float>>>);
|
||||
static_assert(!has_type_member<common_type<int, simd::vec<float>>>);
|
||||
}
|
||||
|
||||
#if defined __AVX__ && !defined __AVX2__
|
||||
@@ -90,7 +90,7 @@ static_assert( std::convertible_to<Ic<1>, simd::vec<float>>);
|
||||
static_assert(!std::convertible_to<Ic<1.1>, simd::vec<float>>);
|
||||
static_assert(!std::convertible_to<simd::vec<int, 4>, simd::vec<float, 4>>);
|
||||
static_assert(!std::convertible_to<simd::vec<float, 4>, simd::vec<int, 4>>);
|
||||
static_assert( std::convertible_to<int, simd::vec<float>>);
|
||||
static_assert(!std::convertible_to<int, simd::vec<float>>);
|
||||
static_assert( std::convertible_to<simd::vec<int, 4>, simd::vec<double, 4>>);
|
||||
|
||||
template <typename V>
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace math_tests
|
||||
concept has_deduced_vec = requires { typename simd::__deduced_vec_t<T>; };
|
||||
|
||||
static_assert(!has_common_type<vf2, vf4>);
|
||||
static_assert( has_common_type<int, vf2>);
|
||||
static_assert(!has_common_type<int, vf2>);
|
||||
|
||||
template <typename T, bool Strict = false>
|
||||
struct holder
|
||||
|
||||
Reference in New Issue
Block a user