blob: 6ef895a45fdfcd30b5179ebb73007a59e6c2f3ec [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_COMPLEX_CLANG_H
#define EIGEN_COMPLEX_CLANG_H
// IWYU pragma: private
#include "../../InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
template <typename RealScalar, int N>
struct complex_packet_wrapper {
using RealPacketT = detail::VectorType<RealScalar, 2 * N>;
complex_packet_wrapper() = default;
EIGEN_STRONG_INLINE explicit complex_packet_wrapper(const RealPacketT& a) : v(a) {}
EIGEN_STRONG_INLINE constexpr std::complex<RealScalar> operator[](Index i) const {
return std::complex<RealScalar>(v[2 * i], v[2 * i + 1]);
}
RealPacketT v;
};
// --- Primary complex packet aliases ---
constexpr int kComplexFloatSize = kFloatPacketSize / 2; // 2, 4, or 8
constexpr int kComplexDoubleSize = kDoublePacketSize / 2; // 1, 2, or 4
using PacketXcf = complex_packet_wrapper<float, kComplexFloatSize>;
using PacketXcd = complex_packet_wrapper<double, kComplexDoubleSize>;
struct generic_complex_packet_traits : default_packet_traits {
enum {
Vectorizable = 1,
AlignedOnScalar = 1,
HasAdd = 1,
HasSub = 1,
HasMul = 1,
HasDiv = 1,
HasNegate = 1,
HasAbs = 0,
HasAbs2 = 0,
HasMin = 0,
HasMax = 0,
HasArg = 0,
HasSetLinear = 0,
HasConj = 1,
// Math functions
HasLog = 1,
HasExp = 1,
HasSqrt = 1,
};
};
template <>
struct packet_traits<std::complex<float>> : generic_complex_packet_traits {
using type = PacketXcf;
using half = PacketXcf;
enum {
size = kComplexFloatSize,
};
};
template <>
struct unpacket_traits<PacketXcf> : generic_unpacket_traits {
using type = std::complex<float>;
using half = PacketXcf;
using as_real = PacketXf;
enum {
size = kComplexFloatSize,
};
};
template <>
struct packet_traits<std::complex<double>> : generic_complex_packet_traits {
using type = PacketXcd;
using half = PacketXcd;
enum {
size = kComplexDoubleSize,
};
};
template <>
struct unpacket_traits<PacketXcd> : generic_unpacket_traits {
using type = std::complex<double>;
using half = PacketXcd;
using as_real = PacketXd;
enum {
size = kComplexDoubleSize,
};
};
// ------------ Load and store ops ----------
#define EIGEN_CLANG_COMPLEX_LOAD_STORE(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE ploadu<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
return PACKET_TYPE(ploadu<typename unpacket_traits<PACKET_TYPE>::as_real>(&numext::real_ref(*from))); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pload<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
return PACKET_TYPE(pload<typename unpacket_traits<PACKET_TYPE>::as_real>(&numext::real_ref(*from))); \
} \
template <> \
EIGEN_STRONG_INLINE void pstoreu<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
typename unpacket_traits<PACKET_TYPE>::type * to, const PACKET_TYPE& from) { \
pstoreu(&numext::real_ref(*to), from.v); \
} \
template <> \
EIGEN_STRONG_INLINE void pstore<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
typename unpacket_traits<PACKET_TYPE>::type * to, const PACKET_TYPE& from) { \
pstore(&numext::real_ref(*to), from.v); \
}
EIGEN_CLANG_COMPLEX_LOAD_STORE(PacketXcf);
EIGEN_CLANG_COMPLEX_LOAD_STORE(PacketXcd);
#undef EIGEN_CLANG_COMPLEX_LOAD_STORE
namespace detail {
// Index sequence over the real components -- two per complex value -- of a
// complex packet. Index Is names component Is % 2 of complex value Is / 2.
template <typename ComplexPacket>
using complex_real_indices = vector_indices<typename ComplexPacket::RealPacketT>;
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pset1_impl(const typename unpacket_traits<ComplexPacket>::type& from,
std::index_sequence<Is...>) {
using RealPacket = typename ComplexPacket::RealPacketT;
using RealScalar = scalar_type_of_vector_t<RealPacket>;
const RealScalar re = numext::real(from);
const RealScalar im = numext::imag(from);
return ComplexPacket(RealPacket{(Is % 2 == 0 ? re : im)...});
}
// Negates the imaginary parts by flipping their sign bits with one xor; the
// negate-and-shuffle alternative costs an extra blend.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pconj_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
using RealScalar = scalar_type_of_vector_t<typename ComplexPacket::RealPacketT>;
using IntScalar = std::conditional_t<sizeof(RealScalar) == 4, int32_t, int64_t>;
using IntPacket = VectorType<IntScalar, sizeof...(Is)>;
const IntPacket sign_mask = {(Is % 2 == 0 ? IntScalar(0) : (std::numeric_limits<IntScalar>::min)())...};
return ComplexPacket(
reinterpret_cast<typename ComplexPacket::RealPacketT>(reinterpret_cast<IntPacket>(a.v) ^ sign_mask));
}
// {re, im} -> {im, re}.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pcplxflip_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2) + (1 - Is % 2))...));
}
// {re, im} -> {re, re}.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pdupreal_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2))...));
}
// {re, im} -> {im, im}.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pdupimag_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2) + 1)...));
}
// (a + ib)(c + id) = (ac - bd) + i(ad + bc), as an even/odd merge of the
// difference and sum vectors. Both products are spelled inside one expression
// each so fp-contract may fuse them, and the merge of a same-operand sub and
// add is the pattern LLVM folds into a single fmaddsub-style instruction on
// targets that have one. This matches the handwritten x86 backends, unlike
// the conjugate-and-flip formulation of the generic pmul_complex.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_pmul_impl(const ComplexPacket& x, const ComplexPacket& y,
std::index_sequence<Is...> is) {
using RealPacket = typename ComplexPacket::RealPacketT;
const RealPacket x_re = complex_pdupreal_impl(x, is).v;
const RealPacket t = complex_pdupimag_impl(x, is).v * complex_pcplxflip_impl(y, is).v;
const RealPacket sub = x_re * y.v - t;
const RealPacket add = x_re * y.v + t;
return ComplexPacket(__builtin_shufflevector(sub, add, (Is % 2 == 0 ? Is : sizeof...(Is) + Is)...));
}
// Loads each complex value Repeat times in a row: Repeat == 2 implements
// ploaddup and Repeat == 4 implements ploadquad.
template <std::size_t Repeat, typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_loadrepeat_impl(const typename unpacket_traits<ComplexPacket>::type* from,
std::index_sequence<Is...>) {
using RealPacket = typename ComplexPacket::RealPacketT;
return ComplexPacket(
RealPacket{(Is % 2 == 0 ? numext::real(from[Is / (2 * Repeat)]) : numext::imag(from[Is / (2 * Repeat)]))...});
}
// Reverses the complex values, keeping each real/imaginary pair together.
template <typename ComplexPacket, std::size_t... Is>
EIGEN_STRONG_INLINE ComplexPacket complex_preverse_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
constexpr std::size_t kLastValue = sizeof...(Is) - 2;
return ComplexPacket(__builtin_shufflevector(a.v, a.v, (kLastValue - 2 * (Is / 2) + Is % 2)...));
}
} // namespace detail
// --- pset1 for complex ---
#define EIGEN_CLANG_COMPLEX_SET1(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pset1<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type& from) { \
return detail::complex_pset1_impl<PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
}
EIGEN_CLANG_COMPLEX_SET1(PacketXcf)
EIGEN_CLANG_COMPLEX_SET1(PacketXcd)
#undef EIGEN_CLANG_COMPLEX_SET1
// ----------- Unary ops ------------------
#define DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, OP) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE OP<PACKET_TYPE>(const PACKET_TYPE& a) { \
return PACKET_TYPE(OP(a.v)); \
}
#define EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PACKET_TYPE) \
DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, pnegate) \
DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, pzero) \
template <> \
EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type pfirst<PACKET_TYPE>(const PACKET_TYPE& a) { \
return a[0]; \
} \
EIGEN_INSTANTIATE_COMPLEX_MATH_FUNCS(PACKET_TYPE)
EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PacketXcf);
EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PacketXcd);
#undef DELEGATE_UNARY_TO_REAL_OP
#undef EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS
// --- Operations that rearrange the real and imaginary lanes ---
#define EIGEN_CLANG_COMPLEX_LANE_OPS(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pconj<PACKET_TYPE>(const PACKET_TYPE& a) { \
return detail::complex_pconj_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pcplxflip<PACKET_TYPE>(const PACKET_TYPE& a) { \
return detail::complex_pcplxflip_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pdupreal<PACKET_TYPE>(const PACKET_TYPE& a) { \
return detail::complex_pdupreal_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pdupimag<PACKET_TYPE>(const PACKET_TYPE& a) { \
return detail::complex_pdupimag_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
}
EIGEN_CLANG_COMPLEX_LANE_OPS(PacketXcf)
EIGEN_CLANG_COMPLEX_LANE_OPS(PacketXcd)
#undef EIGEN_CLANG_COMPLEX_LANE_OPS
// --- ploaddup and ploadquad ---
#define EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE ploaddup<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
return detail::complex_loadrepeat_impl<2, PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE ploadquad<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
return detail::complex_loadrepeat_impl<4, PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
}
EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PacketXcf)
EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PacketXcd)
#undef EIGEN_CLANG_COMPLEX_LOAD_REPEAT
// --- preverse ---
template <>
EIGEN_STRONG_INLINE PacketXcf preverse<PacketXcf>(const PacketXcf& a) {
return detail::complex_preverse_impl(a, detail::complex_real_indices<PacketXcf>{});
}
template <>
EIGEN_STRONG_INLINE PacketXcd preverse<PacketXcd>(const PacketXcd& a) {
return detail::complex_preverse_impl(a, detail::complex_real_indices<PacketXcd>{});
}
// ----------- Binary ops ------------------
#define DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, OP) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE OP<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
return PACKET_TYPE(OP(a.v, b.v)); \
}
#define EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PACKET_TYPE) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, padd) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, psub) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pand) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, por) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pxor) \
DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pandnot) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pdiv<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
return pdiv_complex(a, b); \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pcmp_eq<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
const PACKET_TYPE t = PACKET_TYPE(pcmp_eq(a.v, b.v)); \
return PACKET_TYPE(pand(pdupreal(t).v, pdupimag(t).v)); \
}
EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PacketXcf);
EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PacketXcd);
// The sub-packets complex_predux_mul in Reductions.h recurses through call
// detail::complex_pmul_impl directly, so only the full packets need pmul.
#define EIGEN_CLANG_COMPLEX_PMUL(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pmul<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
return detail::complex_pmul_impl(a, b, detail::complex_real_indices<PACKET_TYPE>{}); \
}
EIGEN_CLANG_COMPLEX_PMUL(PacketXcf);
EIGEN_CLANG_COMPLEX_PMUL(PacketXcd);
#define EIGEN_CLANG_PACKET_SCATTER_GATHER(PACKET_TYPE) \
template <> \
EIGEN_STRONG_INLINE void pscatter(unpacket_traits<PACKET_TYPE>::type* to, const PACKET_TYPE& from, Index stride) { \
constexpr int size = unpacket_traits<PACKET_TYPE>::size; \
for (int i = 0; i < size; ++i) { \
to[i * stride] = from[i]; \
} \
} \
template <> \
EIGEN_STRONG_INLINE PACKET_TYPE pgather<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
const unpacket_traits<PACKET_TYPE>::type* from, Index stride) { \
constexpr int size = unpacket_traits<PACKET_TYPE>::size; \
PACKET_TYPE result; \
for (int i = 0; i < size; ++i) { \
const unpacket_traits<PACKET_TYPE>::type from_i = from[i * stride]; \
result.v[2 * i] = numext::real(from_i); \
result.v[2 * i + 1] = numext::imag(from_i); \
} \
return result; \
}
EIGEN_CLANG_PACKET_SCATTER_GATHER(PacketXcf);
EIGEN_CLANG_PACKET_SCATTER_GATHER(PacketXcd);
#undef EIGEN_CLANG_PACKET_SCATTER_GATHER
#undef DELEGATE_BINARY_TO_REAL_OP
#undef EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS
#undef EIGEN_CLANG_COMPLEX_PMUL
// ------------ ternary ops -------------
// The mask duplicates each complex value's bits across both real lanes, so
// selecting per real lane is equivalent to selecting whole complex values.
template <>
EIGEN_STRONG_INLINE PacketXcf pselect<PacketXcf>(const PacketXcf& mask, const PacketXcf& a, const PacketXcf& b) {
return PacketXcf(pselect(mask.v, a.v, b.v));
}
template <>
EIGEN_STRONG_INLINE PacketXcd pselect<PacketXcd>(const PacketXcd& mask, const PacketXcd& a, const PacketXcd& b) {
return PacketXcd(pselect(mask.v, a.v, b.v));
}
// --- zip_in_place for complex ---
namespace detail {
// Complex packets interleave whole complex values, so their real and imaginary
// components move together.
template <>
EIGEN_ALWAYS_INLINE void zip_in_place<PacketXcf>(PacketXcf& p1, PacketXcf& p2) {
zip_in_place_impl<2>(p1.v, p2.v, complex_real_indices<PacketXcf>{});
}
#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
// PacketXcd holds a single complex value at 16 bytes, so there is nothing to interleave.
template <>
EIGEN_ALWAYS_INLINE void zip_in_place<PacketXcd>(PacketXcd& p1, PacketXcd& p2) {
zip_in_place_impl<2>(p1.v, p2.v, complex_real_indices<PacketXcd>{});
}
#endif
} // namespace detail
// --- ptranspose for complex ---
// PacketXcf: valid block sizes depend on kComplexFloatSize.
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 2>& kernel) {
detail::ptranspose_impl(kernel);
}
#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 4>& kernel) {
detail::ptranspose_impl(kernel);
}
#endif
#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 64
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 8>& kernel) {
detail::ptranspose_impl(kernel);
}
#endif
// PacketXcd: valid block sizes depend on kComplexDoubleSize.
#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcd, 2>& kernel) {
detail::ptranspose_impl(kernel);
}
#endif
#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 64
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcd, 4>& kernel) {
detail::ptranspose_impl(kernel);
}
#endif
EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(PacketXcf, PacketXf)
EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(PacketXcd, PacketXd)
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_COMPLEX_CLANG_H