Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made scan a new format callable #2062

Merged
merged 11 commits into from
Feb 16, 2025
28 changes: 25 additions & 3 deletions include/eve/concept/generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,34 @@ namespace eve
//! @concept generator
//! The concept `generator<Constant>` is satisfied if `Constant` is built as a eve::constant_callable.
//!
//! @tparam G Type of the generator to check.
//!
//! @groupheader{Examples}
//! - `eve::one`
//! - The type of `eve::one`
//====================================================================================================================
template<typename Constant>
concept generator = requires { typename Constant::constant_callable_tag; };
//================================================================================================
//====================================================================================================================
//! @}
//====================================================================================================================

//====================================================================================================================
//! @ingroup simd_concepts
//! @{
//! @concept generator_from
//! The concept `generator_for` is satisfied if `G` satisfies eve::generator and can be called with a
//! `eve::as<T>` argument.
//!
//! @tparam G Type of the generator to check.
//! @tparam T Type of the argument to pass to the generator.
//!
//! @groupheader{Examples}
//! - The type of `eve::one` satisfies `generator_for<int>`
//! - The type of `eve::nbmantissabits` satisfies `generator_for<float>` but not `generator_for<int>`
//====================================================================================================================
template<typename G, typename T>
concept generator_for = generator<G> && requires(G g, as<T> t) { g(t); };
//====================================================================================================================
//! @}
//================================================================================================
//====================================================================================================================
}
32 changes: 32 additions & 0 deletions include/eve/concept/invocable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,36 @@ concept invocable = requires(F&& f, Args&&...args)
//! @}
//================================================================================================

//================================================================================================
//! @ingroup simd_concepts
//! @{
//! @brief Specifies that the function `Op`, when called with arguments of types `Args...`,
//! returns a value convertible to `R`.
//!
//! @tparam Op The function type
//! @tparam R The expected return type
//! @tparam Args The arguments types
//================================================================================================
template <typename Op, typename R, typename... Args>
concept invocable_returning = requires (Op op, Args&& ... args) {
{ op(std::forward<Args>(args)...) } -> std::convertible_to<R>;
};
//================================================================================================
//! @}
//================================================================================================


//================================================================================================
//! @ingroup simd_concepts
//! @{
//! @brief Specifies that `Op` is a monoid function operating on values of type `T`.
//!
//! @tparam Op
//! @tparam T
//================================================================================================
template <typename Op, typename T>
concept monoid = invocable_returning<Op, T, T, T>;
//================================================================================================
//! @}
//================================================================================================
}
31 changes: 31 additions & 0 deletions include/eve/concept/substitute.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//==================================================================================================
/*
EVE - Expressive Vector Engine
Copyright : EVE Project Contributors
SPDX-License-Identifier: BSL-1.0
*/
//==================================================================================================
#pragma once

#include <concepts>
#include <eve/concept/value.hpp>
#include <eve/concept/generator.hpp>

namespace eve
{
//================================================================================================
//! @ingroup simd_concepts
//! @{
//! @concept substitute_for
//! @brief Specify that a type can be used as a substitute for another type after calling
//! `eve::as_value` on a value of the first type.
//!
//! @tparam Substitute Type of the substitute value.
//! @tparam Target Type of the target value that will result from the `eve::as_value` call.
//================================================================================================
template<typename Substitute, typename Target>
concept substitute_for = generator_for<Substitute, Target> || std::constructible_from<Target, Substitute>;
//================================================================================================
//! @}
//================================================================================================
}
77 changes: 33 additions & 44 deletions include/eve/module/core/regular/impl/scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,48 @@

namespace eve::detail
{
template<int group_size, simd_value Wide, typename Op>
EVE_FORCEINLINE Wide
scan_common_impl(Wide x, Op op)
{
if constexpr( group_size == 1 ) return x;
else
template<int group_size, simd_value Wide, typename Op>
EVE_FORCEINLINE Wide scan_common_impl(Wide x, Op op)
{
x = scan_common_impl<group_size / 2>(x, op);
return op(x, slide_right(x, eve::index<group_size / 2>));
if constexpr( group_size == 1 ) return x;
else
{
x = scan_common_impl<group_size / 2>(x, op);
return op(x, slide_right(x, eve::index<group_size / 2>));
}
}
}

template<int group_size, simd_value Wide, typename Op>
EVE_FORCEINLINE Wide
scan_common_impl(Wide x, Op op, Wide z)
{
if constexpr( group_size == 1 ) return x;
else
template<int group_size, simd_value Wide, typename Op>
EVE_FORCEINLINE Wide scan_common_impl(Wide x, Op op, Wide z)
{
x = scan_common_impl<group_size / 2>(x, op, z);
return op(x, slide_right(z, x, eve::index<group_size / 2>));
if constexpr( group_size == 1 ) return x;
else
{
x = scan_common_impl<group_size / 2>(x, op, z);
return op(x, slide_right(z, x, eve::index<group_size / 2>));
}
}
}

template<simd_value Wide, typename Op, typename Zero>
EVE_FORCEINLINE Wide
scan_(EVE_SUPPORTS(cpu_), Wide v, Op op, Zero z) noexcept
{
if constexpr( Wide::size() == 1 ) return v;
else if constexpr( has_emulated_abi_v<Wide> )
template<callable_options O, simd_value Wide, typename Op, typename Zero>
EVE_FORCEINLINE Wide scan_(EVE_REQUIRES(cpu_), O const&, Wide v, Op op, Zero z) noexcept
{
auto sum_value = v.get(0);
if constexpr( Wide::size() == 1 ) return v;
else if constexpr( has_emulated_abi_v<Wide> )
{
auto sum_value = v.get(0);

for( int i = 1; i != Wide::size(); ++i )
for( int i = 1; i != Wide::size(); ++i )
{
sum_value = op(sum_value, v.get(i));
v.set(i, sum_value);
}

return v;
}
else if constexpr( std::same_as<Zero, callable_zero_> )
{
sum_value = op(sum_value, v.get(i));
v.set(i, sum_value);
return scan_common_impl<Wide::size()>(v, op);
}

return v;
}
else if constexpr( std::same_as<Zero, callable_zero_> )
{
return scan_common_impl<Wide::size()>(v, op);
else { return scan_common_impl<Wide::size()>(v, op, as_value(z, as<Wide>{})); }
}
else { return scan_common_impl<Wide::size()>(v, op, Wide {z}); }
}

template<simd_value Wide>
EVE_FORCEINLINE Wide
scan_(EVE_SUPPORTS(cpu_), Wide v) noexcept
{
return scan(v, add, eve::zero);
}

}
20 changes: 11 additions & 9 deletions include/eve/module/core/regular/impl/simd/x86/scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ template<std::ptrdiff_t Shift> struct slide_right_in_lanes_lambda

template<typename T, typename N, std::ptrdiff_t Shift>
EVE_FORCEINLINE wide<T, N>
slide_right_in_lanes(wide<T, N> x, wide<T, N> y, index_t<Shift>) requires(current_api == avx2)
slide_right_in_lanes(wide<T, N> x, wide<T, N> y, index_t<Shift>)
{
if constexpr( is_bundle_v<abi_t<T, N>> )
return wide<T, N> {kumi::map(slide_right_in_lanes_lambda<Shift> {}, x, y)};
Expand Down Expand Up @@ -79,18 +79,20 @@ use_scan_in_lanes(Wide)
else return std::false_type {};
}

template<simd_value Wide, typename Op, typename Zero>
EVE_FORCEINLINE auto
scan_(EVE_SUPPORTS(avx2_), Wide v, Op op, Zero z_) requires(current_api == avx2)
template<callable_options O, typename T, typename N, typename Op, typename Zero>
EVE_FORCEINLINE auto scan_(EVE_REQUIRES(avx2_), O const& opts, wide<T, N> v, Op op, Zero z_)
requires (x86_abi<abi_t<T, N>> && (current_api == avx2))
{
if constexpr( decltype(use_scan_in_lanes(v))::value )
{
Wide z = as_value(z_, eve::as<Wide> {});
constexpr auto size = wide<T, N>::size();

v = scan_in_lanes<Wide::size() / 2>(v, op, z);
Wide left_sum = broadcast(v, index<Wide::size() / 2 - 1>);
return op(v, slide_right(z, left_sum, index<Wide::size() / 2>));
wide<T, N> z = as_value(z_, eve::as<wide<T, N>> {});

v = scan_in_lanes<size / 2>(v, op, z);
wide<T, N> left_sum = broadcast(v, index<size / 2 - 1>);
return op(v, slide_right(z, left_sum, index<size / 2>));
}
else return scan_(EVE_RETARGET(cpu_), v, op, z_);
else return scan.behavior(cpu_{}, opts, v, op, z_);
}
}
106 changes: 69 additions & 37 deletions include/eve/module/core/regular/scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,81 @@
//==================================================================================================
#pragma once

#include <eve/arch.hpp>
#include <eve/detail/overload.hpp>
#include <eve/concept/invocable.hpp>
#include <eve/concept/substitute.hpp>

namespace eve
{
//TODO DOC
//================================================================================================
//! @addtogroup core_simd
//! @{
//! @var scan
//! @brief Computes the TODO
//!
//! @groupheader{Header file}
//!
//! @code
//! #include <eve/module/core.hpp>
//! @endcode
//!
//! @groupheader{Callable Signatures}
//!
//! @code
//! namespace eve
//! {
//! TODO
//! }
//! @endcode
//!
//! **Parameters**
//!
//! * `x`: An instance of an [SIMD value](@ref eve::simd_value)
//!
//! **Return value**
//!
//! * TODO
//!
//! @groupheader{Example}
//!
//! TODO
//! @}
//================================================================================================
EVE_MAKE_CALLABLE(scan_, scan);
template<typename Options>
struct scan_t : callable<scan_t, Options>
{
template<simd_value Wide, eve::monoid<Wide> Op, eve::substitute_for<Wide> Zero>
constexpr EVE_FORCEINLINE Wide operator()(Wide w, Op op, Zero z) const noexcept
{
return EVE_DISPATCH_CALL(w, op, z);
}

template<simd_value Wide>
constexpr EVE_FORCEINLINE Wide operator()(Wide w) const noexcept
{
return EVE_DISPATCH_CALL(w, add, eve::zero);
}

EVE_CALLABLE_OBJECT(scan_t, scan_);
};

//================================================================================================
//! @addtogroup core_simd
//! @{
//! @var scan
//! @brief Computes the generalized prefix sum over a [simd value](@ref eve::simd_value).
//!
//! @groupheader{Header file}
//!
//! @code
//! #include <eve/module/core.hpp>
//! @endcode
//!
//! @groupheader{Callable Signatures}
//!
//! @code
//! namespace eve
//! {
//! template<simd_value Wide, eve::monoid<Wide> Op, eve::substitute_for<Wide> Zero>
//! constexpr Wide scan(Wide auto x, Op op, Zero zero) noexcept; // 1
//!
//! template<simd_value Wide>
//! constexpr Wide scan(Wide x) noexcept; // 2
//! }
//! @endcode
//!
//! **Parameters**
//!
//! * `x`: An instance of an [SIMD value](@ref eve::simd_value)
//! * `op`: The commutative and associative binary operation to apply.
//! * `zero`: The identity/neutral element used by the operation.
//!
//! **Return value**
//!
//! 1. Returns the generalized prefix sum over `x` using the binary operation `op` and the
//! identity element `zero`.
//! 2. Equivalent to `eve::scan(x, eve::add, eve::zero)`.
//!
//! @note
//! Given a binary operation `op`, a call to `eve::scan` is defined only if `op` is
//! associative, commutative, and pure.
//!
//! @groupheader{Example}
//! @godbolt{doc/core/scan.cpp}
//================================================================================================
inline constexpr auto scan = functor<scan_t>;
//================================================================================================
//! @}
//================================================================================================
}

#include <eve/arch.hpp>
#include <eve/module/core/regular/impl/scan.hpp>

#if defined(EVE_INCLUDE_X86_HEADER)
Expand Down
11 changes: 11 additions & 0 deletions test/doc/core/scan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <eve/module/core.hpp>
#include <iostream>

int main()
{
eve::wide x = {1.0, 2.0, 3.0, 4.0};

std::cout << "x: " << x << "\n";
std::cout << "eve::scan(x): " << eve::scan(x) << "\n";
std::cout << "eve::scan(x, eve::mul, eve::one): " << eve::scan(x, eve::mul, eve::one) << "\n";
}
16 changes: 16 additions & 0 deletions test/unit/api/tuple/algorithm/scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
#include <eve/module/core.hpp>
#include "test.hpp"

TTS_CASE_TPL("Check return type of scan", eve::test::scalar::all_types)
<typename T>(tts::type<T>)
{
using w_t = eve::wide<T>;
TTS_EXPR_IS((eve::scan(std::declval<w_t>(), eve::add, eve::zero)), w_t);
};

TTS_CASE("Check return type of scan on tuples")
{
using w_t = eve::wide<kumi::tuple<char, int, double>>;
auto plus = [](auto a, auto b) {
return kumi::map(eve::add, a, b);
};

TTS_EXPR_IS((eve::scan(std::declval<w_t>(), plus, eve::zero)), w_t);
};

TTS_CASE_TPL( "Check behavior of scan", eve::test::scalar::all_types)
<typename T>(tts::type<T>)
Expand Down