blob: 04a364e79f05e0e5eaec32c5023faff313824071 [file]
// 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