diff --git a/.github/workflows/Clang.yml b/.github/workflows/Clang.yml index f1079f0..59fa0dd 100644 --- a/.github/workflows/Clang.yml +++ b/.github/workflows/Clang.yml @@ -22,7 +22,7 @@ jobs: image: ghcr.io/mattkretz/cplusplus-ci/clang${{ matrix.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Run test suite env: diff --git a/.github/workflows/GCC.yml b/.github/workflows/GCC.yml index cc16d12..1cf3c98 100644 --- a/.github/workflows/GCC.yml +++ b/.github/workflows/GCC.yml @@ -22,7 +22,7 @@ jobs: image: ghcr.io/mattkretz/cplusplus-ci/gcc${{ matrix.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Run test suite env: diff --git a/.github/workflows/fair-software.yml b/.github/workflows/fair-software.yml index 87554c6..a061b2c 100644 --- a/.github/workflows/fair-software.yml +++ b/.github/workflows/fair-software.yml @@ -12,7 +12,7 @@ jobs: name: "fair-software" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: repository: mattkretz/howfairis path: howfairis diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index eb598e9..f3e9171 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -8,6 +8,6 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: REUSE Compliance Check - uses: fsfe/reuse-action@v5 + uses: fsfe/reuse-action@v6 diff --git a/README.md b/README.md index b80c2b0..64cdca7 100644 --- a/README.md +++ b/README.md @@ -5,93 +5,98 @@ [![REUSE status](https://github.com/GSI-HPC/simd/actions/workflows/reuse.yml/badge.svg)](https://github.com/GSI-HPC/simd/actions/workflows/reuse.yml) [![fair-software.eu](https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8B%20%20%E2%97%8B%20%20%E2%97%8B%20%20%E2%97%8B-red)](https://fair-software.eu) +## Build, install, use? + +Parts of this implementation are already integrated into GCC 16. +If you want to try the development version you can either install this library via: +```sh +make prefix=~/.local install +``` +or simply point your compiler's include path to this repository. + +Alternatively you can also install into the compiler's standard library +directory: +```sh +make install-system +``` +There is no support for uninstalling, since `install-system` overwrites the `std::simd` headers of libstdc++. + +Build your own project with C++26 (latest GCC, PRs to support Clang are +welcome). Just include `` and you're good to go: +```c++ +#include +``` + +To build the tests there are multiple targets available. `make help` will list all of them. + +## Feature macros + +By default all vectorizable types (including `complex`) are supported. More features can be enabled by defining the following macros: + +| Macro | Description | +|-------|-------------| +| `VIR_EXTENSIONS` | Enable several optimizations and warnings on guaranteed precondition violations. | +| `VIR_PATCH_PERMUTE_DYNAMIC` | Implements [simd.permute.dynamic]. | +| `VIR_PATCH_MATH` | Implements [simd.math]. | +| `VIR_PATCH_IMPROVE_CX` | Implements `abs` and `norm` for `vec>`. Three different approaches `=1`, `=2`, and `=3` make different optimization/code-gen trade-offs. | +| `VIR_PATCH_MISSED_OPT` | Enable hand-written instruction selection for optimization patterns the compiler misses. Includes ktest-based mask reductions and pshufb-based type conversions on x86. | +| `VIR_PATCH_TEST_STORES` | Fix masked stores. | +| `VIR_CONSTEVAL_BROADCAST` | Use `consteval` broadcast constructor for value-preserving conversions: Either the value doesn't change or the program is ill-formed. Peace of mind. | + ## Implementation status | Feature | Status | | ------- | ------ | | [P1928R15](https://wg21.link/P1928R15) std::simd — merge data-parallel types from the Parallelism TS 2 | ✅ done (except math) | -| [P3430R3](https://wg21.link/P3430R3) simd issues: explicit, unsequenced, identity-element position, and members of disabled simd | ✅ done | -| [P3441R2](https://wg21.link/P3441R2) Rename simd_split to simd_chunk | ✅ done | -| [P3287R3](https://wg21.link/P3287R3) Exploration of namespaces for std::simd | ✅ done | -| [P2933R4](https://wg21.link/P2933R4) Extend ⟨bit⟩ header function with overloads for std::simd | ✅ done | | [P2663R7](https://wg21.link/P2663R7) Interleaved complex values support in std::simd | ✅ done (except math) | | [P2664R11](https://wg21.link/P2664R11) Proposal to extend std::simd with permutation API | 🟡 partial | | [P2876R3](https://wg21.link/P2876R3) Proposal to extend std::simd with more constructors and accessors | ✅ done | +| [P2929R2](https://wg21.link/P2929R2) simd_invoke | 🔴 open (still in design phase) | +| [P2933R4](https://wg21.link/P2933R4) Extend ⟨bit⟩ header function with overloads for std::simd | ✅ done | | [P2933R4](https://wg21.link/P2933R4) std::simd overloads for `` header | ✅ done (not optimized) | +| [P2964R2](https://wg21.link/P2964R2) Allowing user-defined types in std::simd | 🔴 open (still in design phase) | +| [P3287R3](https://wg21.link/P3287R3) Exploration of namespaces for std::simd | ✅ done | +| [P3430R3](https://wg21.link/P3430R3) simd issues: explicit, unsequenced, identity-element position, and members of disabled simd | ✅ done | +| [P3440R2](https://wg21.link/P3440R2) Add n_elements named constructor to std::simd | 🔴 open (still in design phase) | +| [P3441R2](https://wg21.link/P3441R2) Rename simd_split to simd_chunk | ✅ done | | [P3480R6](https://wg21.link/P3480R6) std::simd is a range | ✅ done | | [P3691R1](https://wg21.link/P3691R1) Reconsider naming of the namespace for 'std::simd' | ✅ done | +| [P3844R4](https://wg21.link/P3844R4) Reword [simd.math] for consteval conversions | ✅ done | | [P3932R0](https://wg21.link/P3932R0) Fix LWG4470: Fix *integer-from* in [simd] | ✅ done | -| [P4042R0](https://wg21.link/P4042R0) Fix LWG4543: incorrect cast between `simd::vec` and `simd::mask` via conversion to and from impl-defined vector types | ✅ done | -| [P3440R2](https://wg21.link/P3440R2) Add n_elements named constructor to std::simd | 🔴 open (still in design phase) | -| [P2929R2](https://wg21.link/P2929R2) simd_invoke | 🔴 open (still in design phase) | -| [P2964R2](https://wg21.link/P2964R2) Allowing user-defined types in std::simd | 🔴 open (still in design phase) | | [P3973R0](https://wg21.link/P3973R0) bit_cast_as: Element type reinterpretation for std::simd | 🔴 open (still in design phase) | | [P3983R0](https://wg21.link/P3983R0) simd object representation | 🔴 open (still in design phase) | | [P3985R0](https://wg21.link/P3985R0) Concepts for std::simd | 🔴 open (still in design phase) | -| [P3844R4](https://wg21.link/P3844R4) Reword [simd.math] for consteval conversions | ✅ done | -| [P4012R1](https://wg21.link/P4012R1) value-preserving consteval broadcast to simd::vec | 🔴 open | -| [P4042R0](https://wg21.link/P4042R0) Fix LWG4543: incorrect cast between `simd::vec` and `simd::mask` [...] | ✅ done | +| [P4012R1](https://wg21.link/P4012R1) value-preserving consteval broadcast to simd::vec | ✅ done | +| [P4042R0](https://wg21.link/P4042R0) Fix LWG4543: incorrect cast between `simd::vec` and `simd::mask` via conversion to and from impl-defined vector types | ✅ done | | Issue | Status | | ------- | ------ | -| [LWG4385](https://wg21.link/lwg4385) Including `` doesn't provide `std::begin/end` | ✅ done (via inclusion of ``) | -| [LWG4409](https://wg21.link/lwg4409) Constant expression `ranges::size(r)` Constraints and Mandates in [simd] | 🔴 open | -| [LWG4420](https://wg21.link/lwg4420) §[simd] conversions (constructor, load, stores, gather, and scatter) are incorrectly constrained for `` types | ✅ done | -| [LWG4470](https://wg21.link/lwg4470) The use of *integer-from*`` all over [simd] is incorrect for `Bytes=sizeof(complex)` | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | -| [LWG4535](https://wg21.link/lwg4535) Disallow user specialization of `` templates | ✅ done (not actionable) | -| [LWG4414](https://wg21.link/lwg4414) §[simd.expos.abi] *deduce-abi-t* is underspecified and incorrectly referenced from `rebind` and `resize` | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | -| [LWG4412](https://wg21.link/lwg4412) Fix declaration of `zero_element` and `uninit_element` | ✅ done | -| [LWG4238](https://wg21.link/lwg4238) `simd_mask>::operator+/-/~` return a disabled simd specialization | ✅ done | +| [LWG4230](https://wg21.link/lwg4230) `simd::real/imag` is overconstrained | ✅ done | | [LWG4231](https://wg21.link/lwg4231) `datapar::chunk` should use *simd-size-type* instead of `size_t` | ✅ done | -| [LWG4518](https://wg21.link/lwg4518) `simd::cat` return type requires inefficient ABI tag change/conversion | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | -| [LWG4413](https://wg21.link/lwg4413) Unused/left-over `simd::alignment` specialization for `basic_mask` | ✅ done | | [LWG4232](https://wg21.link/lwg4232) `datapar::resize` does not resize | ✅ done | -| [LWG4543](https://wg21.link/lwg4543) Incorrect cast between `simd::vec` and `simd::mask` via conversion to and from impl-defined vector types | ✅ done ([P4042R0](https://wg21.link/P4042R0)) | -| [LWG4390](https://wg21.link/lwg4390) `simd::basic_vec(U&&)` default template parameter | ⚪ treated as NAD | -| [LWG4403](https://wg21.link/lwg4403) `simd::basic_vec` CTAD misses difference type casting | ✅ done | -| [LWG4407](https://wg21.link/lwg4407) *constexpr-wrapper-like* needs `remove_cvref_t` in `simd::basic_vec` constructor | ✅ done | +| [LWG4238](https://wg21.link/lwg4238) `simd_mask>::operator+/-/~` return a disabled simd specialization | ✅ done | +| [LWG4280](https://wg21.link/lwg4280) `simd::partial_load` uses undefined identifier `T` | 🔴 unclear | +| [LWG4375](https://wg21.link/lwg4375) `std::simd::bit_ceil` should not be `noexcept` | 🔴 unclear | | [LWG4376](https://wg21.link/lwg4376) ABI tag in return type of [simd.mask.unary] is overconstrained | ✅ done | -| [LWG4230](https://wg21.link/lwg4230) `simd::real/imag` is overconstrained | ✅ done | -| [LWG4436](https://wg21.link/lwg4436) `simd` broadcast is overconstrained — `std::cw<0.f>` is not convertible to `simd::vec` | 🔴 unclear | +| [LWG4382](https://wg21.link/lwg4382) The `simd::basic_mask(bool)` overload needs to be more constrained | 🔴 unclear | +| [LWG4385](https://wg21.link/lwg4385) Including `` doesn't provide `std::begin/end` | ✅ done (via inclusion of ``) | +| [LWG4386](https://wg21.link/lwg4386) `std::simd::select(bool c, const T& a, const U& b)` is underconstrained | 🔴 unclear | +| [LWG4390](https://wg21.link/lwg4390) `simd::basic_vec(U&&)` default template parameter | ⚪ treated as NAD | | [LWG4391](https://wg21.link/lwg4391) Ambiguities of `simd::basic_vec` constructor | 🔴 unclear | -| [LWG4408](https://wg21.link/lwg4408) Hardening `simd::vec::operator[]` | 🔴 unclear | | [LWG4392](https://wg21.link/lwg4392) `simd::unchecked_load` misses difference type casting | 🔴 unclear | -| [LWG4394](https://wg21.link/lwg4394) `simd::unchecked_load(I first, S last)` construct `span` maybe ill-formed | 🔴 unclear | -| [LWG4280](https://wg21.link/lwg4280) `simd::partial_load` uses undefined identifier `T` | 🔴 unclear | | [LWG4393](https://wg21.link/lwg4393) `simd::unchecked_scatter_to` is underconstrained | 🔴 unclear | -| [LWG4386](https://wg21.link/lwg4386) `std::simd::select(bool c, const T& a, const U& b)` is underconstrained | 🔴 unclear | -| [LWG4375](https://wg21.link/lwg4375) `std::simd::bit_ceil` should not be `noexcept` | 🔴 unclear | +| [LWG4394](https://wg21.link/lwg4394) `simd::unchecked_load(I first, S last)` construct `span` maybe ill-formed | 🔴 unclear | | [LWG4402](https://wg21.link/lwg4402) List-initialization of iterators in [simd.mask.overview] | 🔴 unclear | -| [LWG4382](https://wg21.link/lwg4382) The `simd::basic_mask(bool)` overload needs to be more constrained | 🔴 unclear | - - -## Build, install, use? - -This implementation is not intended for use yet. Nevertheless, it should be -usable for experimenting. There is very simple build system support for -installation via: -```sh -make prefix=~/.local install -``` -But you can also just point your compiler's include path to this repository -instead. - -Alternatively you can also install into the compiler's standard library -directory: -```sh -make install-system -``` -You can uninstall again with: -```sh -make uninstall-system -``` - -Build your own project with C++26 (latest GCC, PRs to support Clang are -welcome). Just include `` and you're good to go: -```c++ -#include -``` - -To build the tests there are multiple targets available. `make help` will list -all of them. - +| [LWG4403](https://wg21.link/lwg4403) `simd::basic_vec` CTAD misses difference type casting | ✅ done | +| [LWG4407](https://wg21.link/lwg4407) *constexpr-wrapper-like* needs `remove_cvref_t` in `simd::basic_vec` constructor | ✅ done | +| [LWG4408](https://wg21.link/lwg4408) Hardening `simd::vec::operator[]` | 🔴 unclear | +| [LWG4409](https://wg21.link/lwg4409) Constant expression `ranges::size(r)` Constraints and Mandates in [simd] | 🔴 open | +| [LWG4412](https://wg21.link/lwg4412) Fix declaration of `zero_element` and `uninit_element` | ✅ done | +| [LWG4413](https://wg21.link/lwg4413) Unused/left-over `simd::alignment` specialization for `basic_mask` | ✅ done | +| [LWG4414](https://wg21.link/lwg4414) §[simd.expos.abi] *deduce-abi-t* is underspecified and incorrectly referenced from `rebind` and `resize` | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | +| [LWG4420](https://wg21.link/lwg4420) §[simd] conversions (constructor, load, stores, gather, and scatter) are incorrectly constrained for `` types | ✅ done | +| [LWG4436](https://wg21.link/lwg4436) `simd` broadcast is overconstrained — `std::cw<0.f>` is not convertible to `simd::vec` | 🔴 unclear | +| [LWG4470](https://wg21.link/lwg4470) The use of *integer-from*`` all over [simd] is incorrect for `Bytes=sizeof(complex)` | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | +| [LWG4518](https://wg21.link/lwg4518) `simd::cat` return type requires inefficient ABI tag change/conversion | ✅ done ([P3932R0](https://wg21.link/P3932R0)) | +| [LWG4535](https://wg21.link/lwg4535) Disallow user specialization of `` templates | ✅ done (not actionable) | +| [LWG4543](https://wg21.link/lwg4543) Incorrect cast between `simd::vec` and `simd::mask` via conversion to and from impl-defined vector types | ✅ done ([P4042R0](https://wg21.link/P4042R0)) | diff --git a/codegen/select.c++ b/codegen/select.c++ index bcd3e2b..184275c 100644 --- a/codegen/select.c++ +++ b/codegen/select.c++ @@ -50,7 +50,7 @@ V0 test0b(V0::mask_type a) { return simd::select(a, char(1), char(0)); } V0 test0c(V0::mask_type a) -{ return simd::select(a, V0(1), V0()); } +{ return simd::select(a, V0(char(1)), V0()); } V0 test0d(V0 a, V0 b, V0 c) { return simd::select(a == a, b, c); } @@ -92,7 +92,7 @@ V1 test1b(V1::mask_type a) { return simd::select(a, T1(1), T1()); } V1 test1c(V1::mask_type a) -{ return simd::select(a, V1(1), V1()); } +{ return simd::select(a, V1(T1(1)), V1()); } /* codegen ^"test2a( @@ -123,7 +123,7 @@ V2 test2b(V2::mask_type a) { return simd::select(a, short(1), short()); } V2 test2c(V2::mask_type a) -{ return simd::select(a, V2(1), V2()); } +{ return simd::select(a, V2(short(1)), V2()); } V2 test2d(V2 a, V2 b, V2 c) { return simd::select(a == a, b, c); } diff --git a/constexpr_tests.cpp b/constexpr_tests.cpp index c37f70c..52c8565 100644 --- a/constexpr_tests.cpp +++ b/constexpr_tests.cpp @@ -49,12 +49,18 @@ namespace LWG4420 simd::vec vh2f_b = float16_t(); simd::vec vf2h = {f4, simd::flag_convert}; +#if VIR_CONSTEVAL_BROADCAST simd::vec vf2h_b{float()}; +#endif vh2f = vf2h; vf2h = static_cast(vh2f); +#if VIR_CONSTEVAL_BROADCAST return all_of(vh2f == vh2f_b) && all_of(vf2h == vf2h_b); +#else + return all_of(vh2f == vh2f_b); +#endif }()); } #endif @@ -141,7 +147,11 @@ namespace test02 // ensure 'true ? int : vec' doesn't work template concept has_type_member = requires { typename T::type; }; +#if VIR_CONSTEVAL_BROADCAST static_assert(has_type_member>>); +#else + static_assert(!has_type_member>>); +#endif constexpr simd::vec>::mask_type k = {}; } @@ -176,7 +186,11 @@ static_assert( std::convertible_to, simd::vec>); static_assert(!std::convertible_to, simd::vec>); static_assert(!std::convertible_to, simd::vec>); static_assert(!std::convertible_to, simd::vec>); +#if VIR_CONSTEVAL_BROADCAST static_assert( std::convertible_to>); +#else +static_assert(!std::convertible_to>); +#endif static_assert( std::convertible_to, simd::vec>); template diff --git a/deploy.sh b/deploy.sh index 58c78b0..ae1a9da 100755 --- a/deploy.sh +++ b/deploy.sh @@ -10,10 +10,11 @@ set -e # Source and destination directories SRC_DIR="include/bits" -DEST_DIR="/home/mkretz/src/gcc-simd/libstdc++-v3/include/bits" +DEST_DIR="/home/mkretz/src/gcc-master/libstdc++-v3/include/bits" +TEST_DIR="/home/mkretz/src/gcc-master/libstdc++-v3/testsuite/std/simd" -# Create destination directory if it doesn't exist -mkdir -p "$DEST_DIR" +if ! test -d "$DEST_DIR"; then echo "$DEST_DIR is missing"; fi +if ! test -d "$TEST_DIR"; then echo "$TEST_DIR is missing"; fi filter() { invert=0 @@ -146,22 +147,34 @@ EOF grep '^$' -A100000 } +all_filters() { + filter VIR_EXTENSIONS \ + | filter VIR_PATCH_PERMUTE_DYNAMIC \ + | filter VIR_PATCH_MATH \ + | filter VIR_PATCH_IMPROVE_CX \ + | filter VIR_PATCH_MISSED_OPT \ + | filter VIR_PATCH_TEST_STORES \ + | filter VIR_ASSERT_SANITY \ + | filter VIR_CONSTEVAL_BROADCAST \ + | fix_copyright +} + # Process each .h file in the source directory for file in "$SRC_DIR"/*.h; do if [ -f "$file" ]; then filename=$(basename "$file") echo "Processing $filename..." - - # Use awk to handle VIR_EXTENSIONS conditional blocks - cat "$file" | filter VIR_EXTENSIONS \ - | filter VIR_PATCH_PERMUTE_DYNAMIC \ - | filter VIR_PATCH_MATH \ - | filter VIR_PATCH_IMPROVE_CX \ - | filter VIR_PATCH_MISSED_OPT \ - | filter VIR_PATCH_TEST_STORES \ - | filter VIR_ASSERT_SANITY \ - | fix_copyright > "$DEST_DIR/$filename" + cat "$file" | all_filters > "$DEST_DIR/$filename" fi done -echo "Deployment complete. Processed files copied to $DEST_DIR/" +cat math-traits_tests.cpp | all_filters > "$TEST_DIR/traits_math.cc" +cat constexpr_tests.cpp | all_filters > "$TEST_DIR/traits_common.cc" +cat generic-traits_tests.cpp | all_filters > "$TEST_DIR/traits_impl.cc" +for file in tests/*.cpp; do + filename=$(basename "$file") + echo "Processing $filename..." + cat "$file" | all_filters > "$TEST_DIR/${filename%.cpp}.cc" +done + +echo "Deployment complete. Processed files copied to $DEST_DIR/ and $TEST_DIR/" diff --git a/include/bits/simd_complex.h b/include/bits/simd_complex.h index c1fa99c..d64d3c8 100644 --- a/include/bits/simd_complex.h +++ b/include/bits/simd_complex.h @@ -1102,6 +1102,19 @@ namespace simd // TODO: conversion extensions // [simd.ctor] broadcast constructor ------------------------------------ +#if !VIR_CONSTEVAL_BROADCAST + template <__broadcast_constructible _Up> + [[__gnu__::__always_inline__]] + constexpr + basic_vec(_Up&& __x) noexcept + : _M_data([&](int __i) { + if constexpr (__complex_like<_Up>) + return (__i & 1) == 0 ? __x.real() : __x.imag(); + else + return (__i & 1) == 0 ? __x : _T0(); + }) + {} +#else template <__explicitly_convertible_to _Up> [[__gnu__::__always_inline__]] constexpr explicit(!__broadcast_constructible<_Up, value_type>) @@ -1119,6 +1132,7 @@ namespace simd basic_vec(const _Up& __x) : basic_vec(__value_preserving_cast(__x)) {} +#endif // [simd.ctor] conversion constructor ----------------------------------- template <__complex_like _Up, typename _UAbi> @@ -2266,6 +2280,22 @@ namespace simd // TODO: conversion extensions // [simd.ctor] broadcast constructor ------------------------------------ +#if !VIR_CONSTEVAL_BROADCAST + template <__broadcast_constructible _Up> + requires __complex_like<_Up> + [[__gnu__::__always_inline__]] + constexpr + basic_vec(_Up&& __x) noexcept + : _M_real(__x.real()), _M_imag(__x.imag()) + {} + + template <__broadcast_constructible _Up> + [[__gnu__::__always_inline__]] + constexpr + basic_vec(_Up&& __x) noexcept + : _M_real(__x), _M_imag() + {} +#else template <__explicitly_convertible_to _Up> requires __complex_like<_Up> [[__gnu__::__always_inline__]] @@ -2286,6 +2316,7 @@ namespace simd basic_vec(const _Up& __x) : _M_real(__x), _M_imag() {} +#endif // [simd.ctor] conversion constructor ----------------------------------- template <__complex_like _Up, typename _UAbi> diff --git a/include/bits/simd_details.h b/include/bits/simd_details.h index ff2c875..2c59605 100644 --- a/include/bits/simd_details.h +++ b/include/bits/simd_details.h @@ -1583,6 +1583,7 @@ namespace simd return static_cast<_To>(__x); } +#if VIR_CONSTEVAL_BROADCAST template concept __simd_vec_bcast_consteval = __explicitly_convertible_to<_From, _To> @@ -1592,6 +1593,7 @@ namespace simd || (is_same_v, int> && is_integral_v<_To>) || (is_same_v, unsigned> && unsigned_integral<_To>)); +#endif /** @internal * std::pair is not trivially copyable, this one is */ diff --git a/include/bits/simd_mask.h b/include/bits/simd_mask.h index 52b49f0..13867c7 100644 --- a/include/bits/simd_mask.h +++ b/include/bits/simd_mask.h @@ -915,11 +915,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); @@ -936,7 +937,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 { @@ -945,7 +946,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; } } } @@ -1041,7 +1042,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()); } } @@ -1138,7 +1139,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; } @@ -1151,7 +1152,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>()); diff --git a/include/bits/simd_math.h b/include/bits/simd_math.h index 5fadaa5..69faa97 100644 --- a/include/bits/simd_math.h +++ b/include/bits/simd_math.h @@ -919,7 +919,7 @@ namespace simd __lerp(_Vp __a, _Vp __b, _Vp __t) noexcept { constexpr _Vp __zero = {}; - constexpr _Vp __one(1); + constexpr _Vp __one(cw<1>); using _Mp = typename _Vp::mask_type; // TODO: benchmark which method of computing the mask is better diff --git a/include/bits/simd_vec.h b/include/bits/simd_vec.h index db2cb0c..88c4532 100644 --- a/include/bits/simd_vec.h +++ b/include/bits/simd_vec.h @@ -318,7 +318,7 @@ namespace simd if constexpr (_S_is_partial) if (__do_sanitize) return __select_impl(mask_type::_S_init(mask_type::_S_implicit_mask), - *this, 0)._M_data; + *this, __canon_value_type())._M_data; return _M_data; } #else @@ -1169,6 +1169,14 @@ namespace simd * * @note The constructor is implicit if the conversion (if any) is value-preserving. */ +#if !VIR_CONSTEVAL_BROADCAST + template <__broadcast_constructible _Up> + [[__gnu__::__always_inline__]] + constexpr + basic_vec(_Up&& __x) noexcept + : _M_data(_DataType() == _DataType() ? static_cast(__x) : value_type()) + {} +#else template <__explicitly_convertible_to _Up> [[__gnu__::__always_inline__]] constexpr explicit(!__broadcast_constructible<_Up, value_type>) @@ -1182,6 +1190,7 @@ namespace simd : _M_data(_DataType() == _DataType() ? __value_preserving_cast(__x) : value_type()) {} +#endif // [simd.ctor] conversion constructor ----------------------------------- template @@ -2257,6 +2266,14 @@ namespace simd { return _M_concat_data(); } // [simd.ctor] broadcast constructor ------------------------------------ +#if !VIR_CONSTEVAL_BROADCAST + template <__broadcast_constructible _Up> + [[__gnu__::__always_inline__]] + constexpr + basic_vec(_Up&& __x) noexcept + : _M_data0(static_cast(__x)), _M_data1(static_cast(__x)) + {} +#else template <__explicitly_convertible_to _Up> [[__gnu__::__always_inline__]] constexpr explicit(!__broadcast_constructible<_Up, value_type>) @@ -2270,6 +2287,7 @@ namespace simd : _M_data0(__value_preserving_cast(__x)), _M_data1(__value_preserving_cast(__x)) {} +#endif // [simd.ctor] conversion constructor ----------------------------------- template diff --git a/lib/hypot2.cpp b/lib/hypot2.cpp index 6009c1e..9913ba8 100644 --- a/lib/hypot2.cpp +++ b/lib/hypot2.cpp @@ -75,8 +75,8 @@ namespace std::simd // NaN inputs to min/max are UB (requires totally_ordered), replace inputs where a NaN output // is needed with precise 3²+4²=5². const M nan = isunordered(x, y); - const V absx = select(nan, V(3), fabs(x)); // no error - const V absy = select(nan, V(4), fabs(y)); // no error + const V absx = select(nan, T(3), fabs(x)); // no error + const V absy = select(nan, T(4), fabs(y)); // no error V hi = max(absx, absy); // no error V lo = min(absx, absy); // no error const auto huge_diff = is_large_diff(hi, lo); @@ -88,11 +88,11 @@ namespace std::simd const V scale_back = rescale_factors(hi, lo); return scale_back * sqrt((lo * lo)._M_assoc_barrier() + hi * hi); } - else if (all_of(isnormal(x) || x == 0) && all_of(isnormal(y) || y == 0)) + else if (all_of(isnormal(x) || x == T(0)) && all_of(isnormal(y) || y == T(0))) { // more likely and cheaper than the branch below - const auto k0 = lo == 0; + const auto k0 = lo == T(0); const auto h0 = hi; - hi = select(hi == 0, V(1), hi); + hi = select(hi == T(0), T(1), hi); const V scale_back = rescale_factors(hi, lo); const V r = scale_back * sqrt((lo * lo)._M_assoc_barrier() + hi * hi); return select(k0, h0, r); @@ -104,7 +104,7 @@ namespace std::simd // slower path to support subnormals // if hi is subnormal, avoid scaling by inf & final mul by 0 // (which yields NaN) by using min() - constexpr V subnorm_scale = 1 / norm_min_v; + constexpr V subnorm_scale = T(1) / norm_min_v; // invert exponent w/o error and w/o using the slow divider // unit: xor inverts the exponent but off by 1. Multiplication // with .5 adjusts for the discrepancy. @@ -131,7 +131,7 @@ namespace std::simd // ISA has FMAs (because h1² + lo is an FMA, but the // intermediate // h1² must be kept) - return select(lo == 0 || nan || inf, fixup, r); + return select(lo == T(0) || nan || inf, fixup, r); } } diff --git a/lib/hypot3.cpp b/lib/hypot3.cpp index b311c1e..f145ca5 100644 --- a/lib/hypot3.cpp +++ b/lib/hypot3.cpp @@ -100,7 +100,7 @@ namespace std::simd // slower path to support subnormals // if hi is subnormal, avoid scaling by inf & final mul by 0 // (which yields NaN) by using min() - constexpr V subnorm_scale = 1 / norm_min_v; + constexpr V subnorm_scale = cw<1> / norm_min_v; // invert exponent w/o error and w/o using the slow divider // unit: xor inverts the exponent but off by 1. Multiplication // with .5 adjusts for the discrepancy. @@ -129,7 +129,7 @@ namespace std::simd // ISA has FMAs (because h1² + lo is an FMA, but the // intermediate // h1² must be kept) - return select(lo == 0 || isunordered(x, y + z) + return select(lo == cw<0> || isunordered(x, y + z) || isinf(absx) || isinf(absy) || isinf(absz), fixup, r); } } diff --git a/lib/support.h b/lib/support.h index a798e19..fe5710f 100644 --- a/lib/support.h +++ b/lib/support.h @@ -179,8 +179,8 @@ namespace std::simd inline V rescale_factors(V& hi, auto&... to_scale) { - constexpr V two = 2; - constexpr V half = 1 / two; + constexpr V two = cw<2>; + constexpr V half = cw<1> / two; const V hi_exp = hi & inf_v; // round down to next power-of-2 = 2^(1-n) = 2*2^-n const V scale = hi_exp ^ inf_v; // = 2/hi_exp = 2^n hi = (hi & mantissa_mask_v) | two; // = hi * scale diff --git a/math-traits_tests.cpp b/math-traits_tests.cpp index d38ab2b..c16943c 100644 --- a/math-traits_tests.cpp +++ b/math-traits_tests.cpp @@ -27,7 +27,11 @@ namespace math_tests concept has_deduced_vec = requires { typename simd::__deduced_vec_t; }; static_assert(!has_common_type); +#if VIR_CONSTEVAL_BROADCAST static_assert( has_common_type); +#else + static_assert(!has_common_type); +#endif template struct holder @@ -68,12 +72,16 @@ namespace math_tests static_assert(lerp_invocable); +#if VIR_CONSTEVAL_BROADCAST static_assert( lerp_invocable); +#else + static_assert(!lerp_invocable); +#endif static_assert([] { vf2 x = 0.f; - int y = 0x2'00'00'04; + float y = 0x2'00'00'04; return simd::lerp(x, y, 0.5f)[0]; - }() == 0x1'00'00'02); + }() == float(0x1'00'00'02)); template concept not_hypot_invocable @@ -91,7 +99,11 @@ namespace math_tests static_assert(hypot_invocable_r, vf2>); static_assert(not_hypot_invocable); +#if VIR_CONSTEVAL_BROADCAST static_assert(hypot_invocable_r); +#else + static_assert(not_hypot_invocable); +#endif static_assert(hypot_invocable_r, vf2>); static_assert(not_hypot_invocable, vf2>); @@ -115,8 +127,10 @@ namespace math_tests // the next doesn't work with the P1928 spec, but it can be made to work static_assert(simd::hypot(simd::basic_vec(std::array{1.1f}), 1.2f)[0] == std::hypot(1.1f, 1.2f)); static_assert(simd::hypot(1.1f, 1.2_f1)[0] == std::hypot(1.1f, 1.2f)); +#if VIR_CONSTEVAL_BROADCAST static_assert(simd::hypot(1, 1.2_f1)[0] == std::hypot(1.f, 1.2f)); static_assert(simd::hypot(1.2_f1, 1)[0] == std::hypot(1.f, 1.2f)); +#endif static_assert(simd::hypot(holder {1.f}, 1.2_f1)[0] == std::hypot(1.f, 1.2f)); // the following must not be valid. if you want vec be explicit about it: static_assert(not_hypot_invocable>); diff --git a/tests/arithmetic.cpp b/tests/arithmetic.cpp index 79bb2f1..e629ed9 100644 --- a/tests/arithmetic.cpp +++ b/tests/arithmetic.cpp @@ -89,7 +89,8 @@ template }; ADD_TEST(multiplication) { - std::tuple {V(), V(RealV(1), RealV()), V(RealV(), RealV(1)), init_vec}, + std::tuple {V(), V(RealV(Real(1)), RealV()), V(RealV(), RealV(Real(1))), + init_vec}, [](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 t.verify_equal(y, x - T(1)); t.verify_equal(x - x, y); t.verify_equal(x = z - x, init_vec); - t.verify_equal(x = z - x, V(1)); + t.verify_equal(x = z - x, T(1)); t.verify_equal(z -= x, init_vec); t.verify_equal(z, init_vec); - t.verify_equal(z -= z, V(0)); - t.verify_equal(z, V(0)); + t.verify_equal(z -= z, V()); + t.verify_equal(z, V()); } }; @@ -291,15 +292,15 @@ template std::tuple{T(2), init_vec, init_vec, init_vec, init_vec}, [](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 ref = init_vec; t.verify_equal(z / x, ref); ref = init_vec; t.verify_equal(a / x, ref); - t.verify_equal(b / b, V(1)); + t.verify_equal(b / b, T(1)); ref = init_vec; t.verify_equal(x / b, ref); t.verify_equal(x /= b, ref); diff --git a/tests/complex-math.cpp b/tests/complex-math.cpp index 91b60b4..8a269ff 100644 --- a/tests/complex-math.cpp +++ b/tests/complex-math.cpp @@ -31,7 +31,7 @@ template ADD_TEST(Norm) { std::tuple {test_iota}, [](auto& t, const V x) { - const V y = {x.real(), x.real() / 3}; + const V y = {x.real(), x.real() / std::cw<3>}; t.verify_equal(norm(x), RV([&](int i) { return std::norm(x[i]); })); t.verify_equal(norm(y), RV([&](int i) { return std::norm(y[i]); })); } @@ -42,7 +42,7 @@ template [](auto& t, const V x) { if !consteval { - const V y = {x.real(), x.real() / 3}; + const V y = {x.real(), x.real() / std::cw<3>}; t.verify_equal(abs(x), RV([&](int i) { return std::abs(x[i]); })); t.verify_equal(abs(y), RV([&](int i) { return std::abs(y[i]); })); } diff --git a/tests/mask.cpp b/tests/mask.cpp index 474051d..51e6671 100644 --- a/tests/mask.cpp +++ b/tests/mask.cpp @@ -58,14 +58,14 @@ template 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); if constexpr (std::is_integral_v) { - 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); } diff --git a/tests/math.cpp b/tests/math.cpp index 2af50bf..24a008e 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -59,7 +59,7 @@ template }; ADD_TEST(hypot) { - std::tuple {(test_iota + 21) / 3}, + std::tuple {(test_iota + std::cw<21>) / std::cw<3>}, [](auto& t, V x) { t.verify_equal_to_ulp(hypot(x, x), V([&](int i) -> T { diff --git a/tests/simd_alg.cpp b/tests/simd_alg.cpp index 1aca663..1d4adf2 100644 --- a/tests/simd_alg.cpp +++ b/tests/simd_alg.cpp @@ -15,8 +15,7 @@ template using M = typename V::mask_type; using pair = std::pair; - static constexpr std::conditional_t, short, T> x_max - = test_iota_max; + static constexpr T x_max = test_iota_max; static constexpr int x_max_int = static_cast(x_max); static constexpr V @@ -28,7 +27,7 @@ template return static_cast(std::to_underlying(x_max) - static_cast(x)); } else - return x_max - x; + return std::cw - x; } ADD_TEST(Select) { diff --git a/tests/unittest_pch.h b/tests/unittest_pch.h index 641b5f0..9205c32 100644 --- a/tests/unittest_pch.h +++ b/tests/unittest_pch.h @@ -1333,7 +1333,8 @@ template fec.record_second(); t2.verify_equal_to_ulp(res, expect, std::cw<1>) ("inputs: {}, {}", x, y) - ("normal||0: {}, {}", isnormal(x) || x == 0, isnormal(y) || y == 0); + ("normal||0: {}, {}", isnormal(x) || x == std::cw<0>, + isnormal(y) || y == std::cw<0>); fec.verify_equal_state(t2)("inputs: {}, {}", x, y) ("result: {} == {::a}", res, res); res = test_ref->tester(y, x);