From 3d85d28aace91aacbf02f95c36bf5bc62cf576da Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Tue, 22 Aug 2023 10:58:41 +0100 Subject: [PATCH] Implements SVE first_true --- include/eve/arch/arm/sve/sve_true.hpp | 12 +++- include/eve/module/core/compress/compress.hpp | 6 +- .../eve/module/core/regular/first_true.hpp | 54 +++++++++++++++- .../regular/impl/simd/arm/sve/first_true.hpp | 62 +++++++++++++++++++ test/doc/core/regular/first_true.cpp | 28 +++++++++ 5 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 include/eve/module/core/regular/impl/simd/arm/sve/first_true.hpp create mode 100644 test/doc/core/regular/first_true.cpp diff --git a/include/eve/arch/arm/sve/sve_true.hpp b/include/eve/arch/arm/sve/sve_true.hpp index 110f364ae7..665776eee9 100644 --- a/include/eve/arch/arm/sve/sve_true.hpp +++ b/include/eve/arch/arm/sve/sve_true.hpp @@ -29,7 +29,7 @@ sve_true() // Returns clear sve_true for type of a given cardinal // while masking potential garbage value template -EVE_FORCEINLINE svbool_t +EVE_FORCEINLINE T sve_true(C cond, as tgt) { if constexpr(C::is_complete && C::is_inverted) @@ -37,8 +37,14 @@ sve_true(C cond, as tgt) using v_t = element_type_t; using fc_t = fundamental_cardinal_t; - if constexpr(T::size() >= fc_t::value ) return sve_true(); - else return keep_first(T::size()).mask(as>{}); + if constexpr ( eve::has_aggregated_abi_v ) + { + using half_t = as_wide_t>; + half_t half = sve_true(cond, eve::as{}); + return T{half, half}; + } + else if constexpr(T::size() == fc_t::value ) return sve_true(); + else return bit_cast(keep_first(T::size()).mask(as>{}), tgt); } else { diff --git a/include/eve/module/core/compress/compress.hpp b/include/eve/module/core/compress/compress.hpp index 6d1165eeb1..06c74a1752 100644 --- a/include/eve/module/core/compress/compress.hpp +++ b/include/eve/module/core/compress/compress.hpp @@ -61,8 +61,10 @@ namespace eve //! template //! auto compress(T x, L m); // (1) //! - //! template - //! auto compress[ignore](T x, L m) // (2) + //! template + //! auto compress[C ignore](T x, L m) // (2) //! } //! @endcode //! diff --git a/include/eve/module/core/regular/first_true.hpp b/include/eve/module/core/regular/first_true.hpp index 60f6132a27..701dec2d11 100644 --- a/include/eve/module/core/regular/first_true.hpp +++ b/include/eve/module/core/regular/first_true.hpp @@ -13,7 +13,54 @@ namespace eve { //================================================================================================ //! @addtogroup core_reduction -// DOC TO DO +//! @{ +//! @var first_true +//! @brief A function to find a first true value, if there is one. +//! +//! @code +//! #include +//! @endcode +//! +//! ## Should you check for any? +//! +//! The function is considering the case when nothing is set to be likely, +//! checking for eve::any before hand is not going to be helpful. +//! At the moment there isn't a function that would do it otherwise. +//! +//! ## What if I know there is a match? +//! We would recommend `*eve::first_true(m)` - this is likely to trigger +//! compiler optimizations, based on it being UB otherwise. +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! template +//! std::optional first_true(L m); // (1) +//! +//! template +//! std::optional first_true(top_bits m); // (2) +//! +//! template +//! std::optional first_true[C ignore](L m); // (3) +//! +//! std::optional first_true(bool m); // (4) +//! @endcode +//! +//! **Parameters** +//! +//! * m - logical_value or top_bits where to find first true value +//! * ignore - ignored elements are considred false. Only supported for +//! `logical_simd_value` +//! +//! **Return value** +//! +//! Returns `std::nullopt` if eve::none(m). +//! Otherwise returns 0 based index. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/core/regular/first_true.cpp} +//! @} //================================================================================================ EVE_MAKE_CALLABLE(first_true_, first_true); } @@ -24,3 +71,8 @@ EVE_MAKE_CALLABLE(first_true_, first_true); #if defined(EVE_INCLUDE_ARM_HEADER) # include #endif + + +#if defined(EVE_INCLUDE_SVE_HEADER) +# include +#endif diff --git a/include/eve/module/core/regular/impl/simd/arm/sve/first_true.hpp b/include/eve/module/core/regular/impl/simd/arm/sve/first_true.hpp new file mode 100644 index 0000000000..30fd7e6b5f --- /dev/null +++ b/include/eve/module/core/regular/impl/simd/arm/sve/first_true.hpp @@ -0,0 +1,62 @@ +//================================================================================================== +/* + EVE - Expressive Vector Engine + Copyright : EVE Project Contributors + SPDX-License-Identifier: BSL-1.0 +*/ +//================================================================================================== +#pragma once + +namespace eve::detail +{ + +// There are some explanations +// Here: https://lemire.me/blog/2022/12/19/implementing-strlen-using-sve/ +// Or: https://www.stonybrook.edu/commcms/ookami/support/_docs/5%20-%20Advanced%20SVE.pdf +template +EVE_FORCEINLINE std::optional + first_true_(EVE_SUPPORTS(sve_), C c, L m) noexcept +{ + if constexpr( C::is_complete && !C::is_inverted ) return std::nullopt; + else if constexpr( has_aggregated_abi_v ) + { + if constexpr( !C::is_complete ) m = m && sve_true(c, as(m)); + auto [lo, hi] = m.slice(); + auto lo_res = first_true[ignore_none](lo); + auto hi_res = first_true[ignore_none](hi); + if( lo_res ) return lo_res; + if( hi_res ) *hi_res += lo.size(); + return hi_res; + } + else + { + auto c_m = L {sve_true(c, eve::as(m))}; + + // We don't need this much but it makes the `no matches` case + // faster + if( !svptest_any(c_m, m) ) return std::nullopt; + + // we need to ignore as false + if constexpr( !C::is_complete ) m = m && c_m; + + eve::as_wide_t> first_true_mask = + svbrkb_z(sve_true>(), m); + return count_true(first_true_mask); + } +} + +template +EVE_FORCEINLINE std::optional + first_true_(EVE_SUPPORTS(sve_), L m) noexcept +{ + return first_true[ignore_none](m); +} + +template +EVE_FORCEINLINE std::optional + first_true_(EVE_SUPPORTS(sve_), top_bits m) noexcept +{ + return first_true(to_logical(m)); +} + +} diff --git a/test/doc/core/regular/first_true.cpp b/test/doc/core/regular/first_true.cpp new file mode 100644 index 0000000000..e4d2207469 --- /dev/null +++ b/test/doc/core/regular/first_true.cpp @@ -0,0 +1,28 @@ +#include +#include + +using w_t = eve::wide>; + +int main() +{ + w_t x = {1, 2, 3, 4}; + + // logicals + + TTS_EQUAL(std::nullopt, eve::first_true(x == 0)); + TTS_EQUAL(0, eve::first_true(x == 1)); + TTS_EQUAL(1, eve::first_true(x == 2)); + + TTS_EQUAL(std::nullopt, eve::first_true[eve::ignore_first(1)](x == 1)); + TTS_EQUAL(2, eve::first_true[eve::ignore_first(2)](x < 5)); + + // top_bits + + eve::top_bits mmask{x >= 2}; + TTS_EQUAL(1, eve::first_true(mmask)); + + // scalar + + TTS_EQUAL(std::nullopt, eve::first_true(false)); + TTS_EQUAL(0, eve::first_true(true)); +}