| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2025 Rasmus Munk Larsen |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #ifndef EIGEN_REDUCTIONS_CLANG_H |
| #define EIGEN_REDUCTIONS_CLANG_H |
| |
| // IWYU pragma: private |
| #include "../../InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| namespace internal { |
| |
| // --- Reductions --- |
| // __builtin_reduce_{min,max} lower well for the integer packets only: for |
| // floating point their strict NaN-ordering semantics scalarize into a serial |
| // compare-blend chain, so PacketXf/PacketXd use the halving trees below. |
| #if EIGEN_HAS_BUILTIN(__builtin_reduce_min) && EIGEN_HAS_BUILTIN(__builtin_reduce_max) |
| #define EIGEN_CLANG_PACKET_REDUX_MINMAX_INT(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_min(const PACKET_TYPE& a) { \ |
| return __builtin_reduce_min(a); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_max(const PACKET_TYPE& a) { \ |
| return __builtin_reduce_max(a); \ |
| } |
| |
| EIGEN_CLANG_PACKET_REDUX_MINMAX_INT(PacketXi) |
| EIGEN_CLANG_PACKET_REDUX_MINMAX_INT(PacketXl) |
| #undef EIGEN_CLANG_PACKET_REDUX_MINMAX_INT |
| #endif |
| |
| #if EIGEN_HAS_BUILTIN(__builtin_reduce_or) |
| // Test the integer view: comparing an all-ones (NaN bit pattern) float mask |
| // against zero is fair game for -ffast-math to fold away, as with pselect. |
| #define EIGEN_CLANG_PACKET_REDUX_ANY(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE bool predux_any(const PACKET_TYPE& a) { \ |
| return __builtin_reduce_or(reinterpret_cast<detail::signed_vector_t<PACKET_TYPE>>(a) != 0) != 0; \ |
| } |
| |
| EIGEN_CLANG_PACKET_REDUX_ANY(PacketXf) |
| EIGEN_CLANG_PACKET_REDUX_ANY(PacketXd) |
| EIGEN_CLANG_PACKET_REDUX_ANY(PacketXi) |
| EIGEN_CLANG_PACKET_REDUX_ANY(PacketXl) |
| #undef EIGEN_CLANG_PACKET_REDUX_ANY |
| #endif |
| |
| #if EIGEN_HAS_BUILTIN(__builtin_reduce_add) && EIGEN_HAS_BUILTIN(__builtin_reduce_mul) |
| #define EIGEN_CLANG_PACKET_REDUX_INT(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return __builtin_reduce_add(a); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_mul<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return __builtin_reduce_mul(a); \ |
| } |
| |
| // __builtin_reduce_{mul,add} are only defined for integer types. |
| EIGEN_CLANG_PACKET_REDUX_INT(PacketXi) |
| EIGEN_CLANG_PACKET_REDUX_INT(PacketXl) |
| #undef EIGEN_CLANG_PACKET_REDUX_INT |
| #endif |
| |
| #if EIGEN_HAS_BUILTIN(__builtin_shufflevector) |
| namespace detail { |
| |
| // Folds `a` in half with `op` until two elements are left and returns them as |
| // (even, odd). Callers combine those two with the same operation; splitting the |
| // final step out is what lets the complex reductions read off the accumulated |
| // real and imaginary parts separately. The halves are shuffled inline rather |
| // than through lower_half/upper_half: an 8-byte half returned by value is |
| // ABI-lowered to a scalar double in IR even under forced inlining, and the |
| // leftover bitcasts perturb LLVM's canonicalization of the commutative fold, |
| // measurably changing the generated code. |
| template <int N> |
| struct halving_reduce { |
| template <typename VectorT, typename Op, std::size_t... Is> |
| static EIGEN_STRONG_INLINE scalar_pair_t<VectorT> fold(const VectorT& a, Op op, std::index_sequence<Is...>) { |
| return halving_reduce<N / 2>::run( |
| op(__builtin_shufflevector(a, a, Is...), __builtin_shufflevector(a, a, (sizeof...(Is) + Is)...)), op); |
| } |
| |
| template <typename VectorT, typename Op> |
| static EIGEN_STRONG_INLINE scalar_pair_t<VectorT> run(const VectorT& a, Op op) { |
| return fold(a, op, std::make_index_sequence<N / 2>{}); |
| } |
| }; |
| |
| template <> |
| struct halving_reduce<2> { |
| template <typename VectorT, typename Op> |
| static EIGEN_STRONG_INLINE scalar_pair_t<VectorT> run(const VectorT& a, Op /*op*/) { |
| return {a[0], a[1]}; |
| } |
| }; |
| |
| template <typename VectorT> |
| EIGEN_STRONG_INLINE scalar_pair_t<VectorT> reduce_add_pairs(const VectorT& a) { |
| return halving_reduce<vector_elements<VectorT>()>::run(a, [](const auto& x, const auto& y) { return x + y; }); |
| } |
| |
| // Folds the packet all the way to a scalar with `op`, which must also be |
| // applicable to bare scalars for the final step. |
| template <typename VectorT, typename Op> |
| EIGEN_STRONG_INLINE scalar_type_of_vector_t<VectorT> tree_reduce(const VectorT& a, Op op) { |
| const scalar_pair_t<VectorT> even_odd = halving_reduce<vector_elements<VectorT>()>::run(a, op); |
| return op(even_odd.first, even_odd.second); |
| } |
| |
| // Multiplies the two halves of a complex packet until two complex values are |
| // left, then multiplies those as scalars. Unlike the reductions above this |
| // cannot work on the real vector, because complex multiplication mixes lanes. |
| template <int N> |
| struct complex_predux_mul { |
| template <typename RealScalar> |
| static EIGEN_STRONG_INLINE std::complex<RealScalar> run(const complex_packet_wrapper<RealScalar, N>& a) { |
| using HalfPacket = complex_packet_wrapper<RealScalar, N / 2>; |
| return complex_predux_mul<N / 2>::run(complex_pmul_impl(HalfPacket(lower_half(a.v)), HalfPacket(upper_half(a.v)), |
| complex_real_indices<HalfPacket>{})); |
| } |
| }; |
| |
| template <> |
| struct complex_predux_mul<2> { |
| template <typename RealScalar> |
| static EIGEN_STRONG_INLINE std::complex<RealScalar> run(const complex_packet_wrapper<RealScalar, 2>& a) { |
| return a[0] * a[1]; |
| } |
| }; |
| |
| template <> |
| struct complex_predux_mul<1> { |
| template <typename RealScalar> |
| static EIGEN_STRONG_INLINE std::complex<RealScalar> run(const complex_packet_wrapper<RealScalar, 1>& a) { |
| return a[0]; |
| } |
| }; |
| |
| } // namespace detail |
| |
| // --- predux and predux_mul for float and double --- |
| // __builtin_reduce_{add,mul} cover the integer packets above but are not |
| // defined for floating point, so these fold the packet by hand. |
| #define EIGEN_CLANG_PACKET_REDUX_FLOAT(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, [](const auto& x, const auto& y) { return x + y; }); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_mul<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, [](const auto& x, const auto& y) { return x * y; }); \ |
| } |
| |
| EIGEN_CLANG_PACKET_REDUX_FLOAT(PacketXf) |
| EIGEN_CLANG_PACKET_REDUX_FLOAT(PacketXd) |
| #undef EIGEN_CLANG_PACKET_REDUX_FLOAT |
| |
| // --- predux_min and predux_max for float and double --- |
| // Also covers the NaN-propagation variants, whose generic fallback spills the |
| // packet to the stack and reduces it with a scalar loop. |
| #define EIGEN_CLANG_PACKET_REDUX_MINMAX_FLOAT(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_min(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmin_op()); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_max(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmax_op()); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_min<PropagateNumbers, PACKET_TYPE>( \ |
| const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmin_num_op()); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_max<PropagateNumbers, PACKET_TYPE>( \ |
| const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmax_num_op()); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_min<PropagateNaN, PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmin_nan_op()); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_max<PropagateNaN, PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return detail::tree_reduce(a, detail::pmax_nan_op()); \ |
| } |
| |
| EIGEN_CLANG_PACKET_REDUX_MINMAX_FLOAT(PacketXf) |
| EIGEN_CLANG_PACKET_REDUX_MINMAX_FLOAT(PacketXd) |
| #undef EIGEN_CLANG_PACKET_REDUX_MINMAX_FLOAT |
| |
| // --- predux and predux_mul for complex --- |
| // The real vector of a complex packet interleaves real and imaginary parts, so |
| // summing it into an (even, odd) pair accumulates the two parts separately. |
| #define EIGEN_CLANG_COMPLEX_REDUX(PACKET_TYPE) \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| const auto re_im = detail::reduce_add_pairs(a.v); \ |
| return unpacket_traits<PACKET_TYPE>::type(re_im.first, re_im.second); \ |
| } \ |
| template <> \ |
| EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type predux_mul<PACKET_TYPE>(const PACKET_TYPE& a) { \ |
| return detail::complex_predux_mul<unpacket_traits<PACKET_TYPE>::size>::run(a); \ |
| } |
| |
| EIGEN_CLANG_COMPLEX_REDUX(PacketXcf) |
| EIGEN_CLANG_COMPLEX_REDUX(PacketXcd) |
| #undef EIGEN_CLANG_COMPLEX_REDUX |
| |
| #endif |
| |
| } // end namespace internal |
| } // end namespace Eigen |
| |
| #endif // EIGEN_REDUCTIONS_CLANG_H |