mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
This implementation differs significantly from the
std::experimental::simd implementation. One goal was a reduction in
template instantiations wrt. what std::experimental::simd did.
Design notes:
- bits/vec_ops.h contains concepts, traits, and functions for working
with GNU vector builtins that are mostly independent from std::simd.
These could move from std::simd:: to std::__vec (or similar). However,
we would then need to revisit naming. For now we kept everything in
the std::simd namespace with __vec_ prefix in the names. The __vec_*
functions can be called unqualified because they can never be called
on user-defined types (no ADL). If we ever get simd<UDT> support this
will be implemented via bit_cast to/from integral vector
builtins/intrinsics.
- bits/simd_x86.h extends vec_ops.h with calls to __builtin_ia32_* that
can only be used after uttering the right GCC target pragma.
- basic_vec and basic_mask are built on top of register-size GNU vector
builtins (for now / x86). Any larger vec/mask is a tree of power-of-2
#elements on the "first" branch. Anything non-power-of-2 that is
smaller than register size uses padding elements that participate in
element-wise operations. The library ensures that padding elements
lead to no side effects. The implementation makes no assumption on the
values of these padding elements since the user can bit_cast to
basic_vec/basic_mask.
Implementation status:
- The implementation is prepared for more than x86 but is x86-only for
now.
- Parts of [simd] *not* implemented in this patch:
- std::complex<floating-point> as vectorizable types
- [simd.permute.dynamic]
- [simd.permute.mask]
- [simd.permute.memory]
- [simd.bit]
- [simd.math]
- mixed operations with vec-mask and bit-mask types
- some conversion optimizations (open questions wrt. missed
optimizations in the compiler)
- This patch implements P3844R3 "Restore simd::vec broadcast from int",
which is not part of the C++26 WD draft yet. If the paper does not get
accepted the feature will be reverted.
- This patch implements D4042R0 "incorrect cast between simd::vec and
simd::mask via conversion to and from impl-defined vector types" (to be
published once the reported LWG issue gets a number).
- The standard feature test macro __cpp_lib_simd is not defined yet.
Tests:
- Full coverage requires testing
1. constexpr,
2. constant-propagating inputs, and
3. unknown (to the optimizer) inputs
- for all vectorizable types
* for every supported width (1–64 and higher)
+ for all possible ISA extensions (combinations)
= with different fast-math flags
... leading to a test matrix that's far out of reach for regular
testsuite builds.
- The tests in testsuite/std/simd/ try to cover all of the API. The
tests can be build in every combination listed above. Per default only
a small subset is built and tested.
- Use GCC_TEST_RUN_EXPENSIVE=something to compile the more expensive
tests (constexpr and const-prop testing) and to enable more /
different widths for the test type.
- Tests can still emit bogus -Wpsabi warnings (see PR98734) which are
filtered out via dg-prune-output.
Benchmarks:
- The current implementation has been benchmarked in some aspects on
x86_64 hardware. There is more optimization potential. However, it is
not always clear whether optimizations should be part of the library
if they can be implemented in the compiler.
- No benchmark code is included in this patch.
libstdc++-v3/ChangeLog:
* include/Makefile.am: Add simd headers.
* include/Makefile.in: Regenerate.
* include/bits/version.def (simd): New.
* include/bits/version.h: Regenerate.
* include/bits/simd_alg.h: New file.
* include/bits/simd_details.h: New file.
* include/bits/simd_flags.h: New file.
* include/bits/simd_iterator.h: New file.
* include/bits/simd_loadstore.h: New file.
* include/bits/simd_mask.h: New file.
* include/bits/simd_mask_reductions.h: New file.
* include/bits/simd_reductions.h: New file.
* include/bits/simd_vec.h: New file.
* include/bits/simd_x86.h: New file.
* include/bits/vec_ops.h: New file.
* include/std/simd: New file.
* testsuite/std/simd/arithmetic.cc: New test.
* testsuite/std/simd/arithmetic_expensive.cc: New test.
* testsuite/std/simd/create_tests.h: New file.
* testsuite/std/simd/creation.cc: New test.
* testsuite/std/simd/creation_expensive.cc: New test.
* testsuite/std/simd/loads.cc: New test.
* testsuite/std/simd/loads_expensive.cc: New test.
* testsuite/std/simd/mask2.cc: New test.
* testsuite/std/simd/mask2_expensive.cc: New test.
* testsuite/std/simd/mask.cc: New test.
* testsuite/std/simd/mask_expensive.cc: New test.
* testsuite/std/simd/reductions.cc: New test.
* testsuite/std/simd/reductions_expensive.cc: New test.
* testsuite/std/simd/shift_left.cc: New test.
* testsuite/std/simd/shift_left_expensive.cc: New test.
* testsuite/std/simd/shift_right.cc: New test.
* testsuite/std/simd/shift_right_expensive.cc: New test.
* testsuite/std/simd/simd_alg.cc: New test.
* testsuite/std/simd/simd_alg_expensive.cc: New test.
* testsuite/std/simd/sse_intrin.cc: New test.
* testsuite/std/simd/stores.cc: New test.
* testsuite/std/simd/stores_expensive.cc: New test.
* testsuite/std/simd/test_setup.h: New file.
* testsuite/std/simd/traits_common.cc: New test.
* testsuite/std/simd/traits_impl.cc: New test.
* testsuite/std/simd/traits_math.cc: New test.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
186 lines
6.9 KiB
C++
186 lines
6.9 KiB
C++
// { dg-do compile { target c++26 } }
|
|
// { dg-require-effective-target x86 }
|
|
|
|
#define _GLIBCXX_SIMD_THROW_ON_BAD_VALUE 1
|
|
|
|
#include <bits/simd_details.h>
|
|
#include <bits/simd_flags.h>
|
|
#include <stdfloat>
|
|
|
|
namespace simd = std::simd;
|
|
|
|
using std::float16_t;
|
|
using std::float32_t;
|
|
using std::float64_t;
|
|
|
|
using namespace std::simd;
|
|
|
|
void test()
|
|
{
|
|
template for (auto t : {float(), double(), float16_t(), float32_t(), float64_t()})
|
|
{
|
|
using T = decltype(t);
|
|
static_assert(__vectorizable<T>);
|
|
}
|
|
|
|
static_assert(!__vectorizable<const float>);
|
|
static_assert(!__vectorizable<float&>);
|
|
static_assert(!__vectorizable<std::bfloat16_t>);
|
|
|
|
template for (constexpr int N : {1, 2, 4, 8})
|
|
{
|
|
static_assert(std::signed_integral<__integer_from<N>>);
|
|
static_assert(sizeof(__integer_from<N>) == N);
|
|
static_assert(__vectorizable<__integer_from<N>>);
|
|
}
|
|
|
|
static_assert(__div_ceil(5, 3) == 2);
|
|
|
|
static_assert(sizeof(_Bitmask<3>) == 1);
|
|
static_assert(sizeof(_Bitmask<30>) == 4);
|
|
|
|
static_assert(__scalar_abi_tag<_ScalarAbi<1>>);
|
|
static_assert(__scalar_abi_tag<_ScalarAbi<2>>);
|
|
static_assert(!__scalar_abi_tag<_Abi_t<1, 1>>);
|
|
|
|
static_assert(__abi_tag<_ScalarAbi<1>>);
|
|
static_assert(__abi_tag<_ScalarAbi<2>>);
|
|
|
|
using AN = decltype(__native_abi<float>());
|
|
using A1 = decltype(__native_abi<float>()._S_resize<1>());
|
|
static_assert(A1::_S_size == 1);
|
|
static_assert(A1::_S_nreg == 1);
|
|
static_assert(A1::_S_variant == AN::_S_variant);
|
|
static_assert(__scalar_abi_tag<A1> == __scalar_abi_tag<AN>);
|
|
static_assert(std::is_same_v<decltype(__abi_rebind<float, AN::_S_size, A1>()), AN>);
|
|
if constexpr (AN::_S_size >= 2) // the target has SIMD support for float
|
|
{
|
|
{
|
|
using A2 = decltype(__abi_rebind<float, 2, AN>());
|
|
static_assert(A2::_S_size == 2);
|
|
static_assert(A2::_S_nreg == 1);
|
|
static_assert(A2::_S_variant == AN::_S_variant);
|
|
using A2x = decltype(__abi_rebind<float, 2, decltype(__abi_rebind<float, 1, A2>())>());
|
|
static_assert(std::is_same_v<A2, A2x>);
|
|
}
|
|
using A4 = decltype(__abi_rebind<float, 4, AN>());
|
|
static_assert(A4::_S_size == 4);
|
|
}
|
|
|
|
static_assert(__streq_to_1("1"));
|
|
static_assert(!__streq_to_1(""));
|
|
static_assert(!__streq_to_1(nullptr));
|
|
static_assert(!__streq_to_1("0"));
|
|
static_assert(!__streq_to_1("1 "));
|
|
|
|
static_assert(__static_sized_range<int[4]>);
|
|
static_assert(__static_sized_range<int[4], 4>);
|
|
static_assert(__static_sized_range<std::array<int, 4>, 4>);
|
|
|
|
static_assert( __value_preserving_convertible_to<int, double>);
|
|
static_assert(!__value_preserving_convertible_to<int, float>);
|
|
static_assert( __value_preserving_convertible_to<float, double>);
|
|
static_assert(!__value_preserving_convertible_to<double, float>);
|
|
|
|
static_assert(__explicitly_convertible_to<float, float16_t>);
|
|
static_assert(__explicitly_convertible_to<long, float16_t>);
|
|
|
|
static_assert(__constexpr_wrapper_like<std::constant_wrapper<2>>);
|
|
static_assert(__constexpr_wrapper_like<std::integral_constant<int, 1>>);
|
|
|
|
static_assert(!__broadcast_constructible<int, float>);
|
|
static_assert(!__broadcast_constructible<int&, float>);
|
|
static_assert(!__broadcast_constructible<int&&, float>);
|
|
static_assert(!__broadcast_constructible<const int&, float>);
|
|
static_assert(!__broadcast_constructible<const int, float>);
|
|
|
|
static_assert(__broadcast_constructible<decltype(std::cw<2>), float>);
|
|
static_assert(__broadcast_constructible<decltype(std::cw<0.f>), std::float16_t>);
|
|
|
|
|
|
static_assert(__higher_rank_than<long, int>);
|
|
static_assert(__higher_rank_than<long long, long>);
|
|
static_assert(__higher_rank_than<int, short>);
|
|
static_assert(__higher_rank_than<short, char>);
|
|
|
|
static_assert(!__higher_rank_than<char, signed char>);
|
|
static_assert(!__higher_rank_than<signed char, char>);
|
|
static_assert(!__higher_rank_than<char, unsigned char>);
|
|
static_assert(!__higher_rank_than<unsigned char, char>);
|
|
|
|
static_assert(__higher_rank_than<unsigned int, short>);
|
|
static_assert(__higher_rank_than<unsigned long, int>);
|
|
static_assert(__higher_rank_than<unsigned long long, long>);
|
|
|
|
static_assert(__higher_rank_than<float, float16_t>);
|
|
static_assert(__higher_rank_than<float32_t, float>);
|
|
static_assert(__higher_rank_than<double, float32_t>);
|
|
static_assert(__higher_rank_than<double, float>);
|
|
static_assert(__higher_rank_than<float64_t, float32_t>);
|
|
static_assert(__higher_rank_than<float64_t, float>);
|
|
static_assert(__higher_rank_than<float64_t, double>);
|
|
|
|
static_assert(__loadstore_convertible_to<float, double>);
|
|
static_assert(__loadstore_convertible_to<int, double>);
|
|
static_assert(!__loadstore_convertible_to<int, float>);
|
|
static_assert(!__loadstore_convertible_to<int, float, __aligned_flag>);
|
|
static_assert(__loadstore_convertible_to<int, float, __convert_flag>);
|
|
static_assert(__loadstore_convertible_to<int, float, __aligned_flag, __convert_flag>);
|
|
|
|
static_assert(__mask_element_size<basic_mask<4>> == 4);
|
|
|
|
static_assert(__highest_bit(0b1000u) == 3);
|
|
static_assert(__highest_bit(0b10000001000ull) == 10);
|
|
}
|
|
|
|
consteval bool
|
|
throws(auto f)
|
|
{
|
|
try { f(); }
|
|
catch (...) { return true; }
|
|
return false;
|
|
}
|
|
|
|
static_assert(!throws([] { __value_preserving_cast<float>(1); }));
|
|
static_assert(!throws([] { __value_preserving_cast<float>(1.5); }));
|
|
static_assert(throws([] { __value_preserving_cast<float>(0x5EAF00D); }));
|
|
static_assert(throws([] { __value_preserving_cast<unsigned>(-1); }));
|
|
static_assert(!throws([] { __value_preserving_cast<unsigned short>(0xffff); }));
|
|
static_assert(throws([] { __value_preserving_cast<unsigned short>(0x10000); }));
|
|
|
|
static_assert(__converts_trivially<int, unsigned>);
|
|
#if __SIZEOF_LONG__ == __SIZEOF_LONG_LONG__
|
|
static_assert(__converts_trivially<long long, long>);
|
|
#elif __SIZEOF_INT__ == __SIZEOF_LONG__
|
|
static_assert(__converts_trivially<int, long>);
|
|
#endif
|
|
static_assert(__converts_trivially<float, float32_t>);
|
|
|
|
static_assert([] {
|
|
bool to_find[10] = {0, 1, 1, 1, 0, 1, 0, 0, 1};
|
|
__bit_foreach(0b100101110u, [&](int i) {
|
|
if (!to_find[i]) throw false;
|
|
to_find[i] = false;
|
|
});
|
|
for (bool b : to_find)
|
|
if (b)
|
|
return false;
|
|
return true;
|
|
}());
|
|
|
|
// flags ////////////////////////
|
|
static_assert(std::is_same_v<decltype(flag_default | flag_default), flags<>>);
|
|
static_assert(std::is_same_v<decltype(flag_convert | flag_default), flags<__convert_flag>>);
|
|
static_assert(std::is_same_v<decltype(flag_convert | flag_convert), flags<__convert_flag>>);
|
|
static_assert(std::is_same_v<decltype(flag_aligned | flag_convert),
|
|
flags<__aligned_flag, __convert_flag>>);
|
|
static_assert(std::is_same_v<decltype(flag_aligned | flag_convert | flag_aligned),
|
|
flags<__aligned_flag, __convert_flag>>);
|
|
static_assert(std::is_same_v<decltype(flag_aligned | (flag_convert | flag_aligned)),
|
|
flags<__aligned_flag, __convert_flag>>);
|
|
|
|
static_assert(!flag_default._S_test(flag_convert));
|
|
static_assert(flag_convert._S_test(flag_convert));
|
|
static_assert(!flag_convert._S_test(flag_aligned));
|
|
static_assert((flag_overaligned<32> | flag_convert | flag_aligned)._S_test(flag_convert));
|