blob: 8d028bed0bbd481d09b013a0dcb37477359be39e [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// 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-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#ifndef EIGEN_MATH_FUNCTIONS_NEON_H
#define EIGEN_MATH_FUNCTIONS_NEON_H
// IWYU pragma: private
#include "../../InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_FLOAT(Packet2f)
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_FLOAT(Packet4f)
#if EIGEN_ARCH_ARM64 && EIGEN_HAS_ARM64_FP16
template <>
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Packet4hf ptanh<Packet4hf>(const Packet4hf& x) {
// Convert to float, call the float ptanh, and then convert back.
return vcvt_f16_f32(ptanh<Packet4f>(vcvt_f32_f16(x)));
}
template <>
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Packet8hf ptanh<Packet8hf>(const Packet8hf& x) {
// Convert each 4 half types to float, call the float ptanh, and then convert back.
return vcombine_f16(vcvt_f16_f32(ptanh<Packet4f>(vcvt_f32_f16(vget_low_f16(x)))),
vcvt_f16_f32(ptanh<Packet4f>(vcvt_f32_f16(vget_high_f16(x)))));
}
#endif // EIGEN_ARCH_ARM64 && EIGEN_HAS_ARM64_FP16
// psqrt and prsqrt have their own kernels below.
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_BF16(Packet4f, Packet4bf)
template <>
EIGEN_STRONG_INLINE Packet4bf psqrt<Packet4bf>(const Packet4bf& a) {
// y = x r with r = FRSQRTE(x), then one FRSQRTS step y <- y (3 - y r) / 2. FRSQRTE is specified
// bit for bit, and rounding y to bfloat16 equals bfloat16(sqrtf(x)) for all 65536 inputs
// (packetmath checks them all); the raw estimate alone misrounds 7366 of them. RSHRN adds half a
// bfloat16 ulp and truncates. That is round half up, which differs from F32ToBf16 only on an
// exact float midpoint, and no input produces one; NaNs stay NaN since their low half is zero.
Packet4f x = Bf16ToF32(a);
Packet4f r = vrsqrteq_f32(x);
Packet4f y = pmul(x, r);
y = pmul(y, vrsqrtsq_f32(y, r));
Packet4bf ybf = vrshrn_n_u32(vreinterpretq_u32_f32(y), 16);
// y is NaN where r is +-inf (x is +-0, or a subnormal the FPU flushes) or +0 (x is +inf), and
// the answer there is the sign and exponent of a. Reading r rather than a keeps that right under
// FPCR.FZ and on ARMv7. The high half of r plus 0x80 is 0x80, 0 or 0x8000 on exactly those
// lanes; every other r, a positive normal or a NaN, has a bit set under 0x7f7f.
uint16x4_t u = vadd_u16(vshrn_n_u32(vreinterpretq_u32_f32(r), 16), vdup_n_u16(0x80));
uint16x4_t step_lane = vtst_u16(u, vdup_n_u16(0x7f7f));
return vbsl_u16(step_lane, ybf, vand_u16(a, vdup_n_u16(0xff80)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf prsqrt<Packet4bf>(const Packet4bf& a) {
// r = FRSQRTE(x) refined by one FRSQRTS step, r1 = r (3 - x r r) / 2, then the same RSHRN
// narrowing as psqrt. r1 rounds to the correctly rounded bfloat16 rsqrt for all 65536 inputs
// (packetmath checks them all). FRSQRTE has 8 fraction bits, so the raw estimate always lands on
// a bfloat16 value or midpoint and misrounds 1270 of them.
Packet4f x = Bf16ToF32(a);
Packet4f r = vrsqrteq_f32(x);
#if EIGEN_ARCH_ARM64
// FMULX gives 2 for 0 * inf where FMUL gives NaN, so no lane needs a select. Where the FPU reads
// x as +-0 (a subnormal too under FPCR.FZ), r is +-inf, y = 2 and the step is -+inf, and |step|
// restores r1 = +-inf. Where x is +inf, r is 0, y = 2 and r1 = 0 * 1.5. Negative and NaN x stay
// NaN. On every other lane FMULX is FMUL and the step is positive, so r1 is the plain step.
Packet4f y = vmulxq_f32(x, r);
Packet4f r1 = pmul(r, vabsq_f32(vrsqrtsq_f32(y, r)));
return vrshrn_n_u32(vreinterpretq_u32_f32(r1), 16);
#else
// ARMv7 has no FMULX. r1 is NaN where x is +-0, +inf or a subnormal, which ARMv7 NEON flushes,
// and there r is already the answer (+-inf, 0, +-inf) with a zero low half, so those lanes take
// r truncated. They are the bit patterns with a - 0x80 >= 0x7f00 unsigned: the exponent-0 range
// wraps around, and +inf, NaN and every negative value sit above.
Packet4f r1 = pmul(r, vrsqrtsq_f32(pmul(x, r), r));
Packet4bf refined = vrshrn_n_u32(vreinterpretq_u32_f32(r1), 16);
Packet4bf raw = vshrn_n_u32(vreinterpretq_u32_f32(r), 16);
uint16x4_t special = vcge_u16(vsub_u16(a, vdup_n_u16(0x80)), vdup_n_u16(0x7f00));
return vbsl_u16(special, raw, refined);
#endif
}
template <>
EIGEN_STRONG_INLINE Packet4bf pfrexp(const Packet4bf& a, Packet4bf& exponent) {
// Both results are exact: the mantissa keeps the input's significand, and the exponent is a
// small integer.
Packet4f fexponent;
const Packet4bf out = F32ToBf16Truncate(pfrexp<Packet4f>(Bf16ToF32(a), fexponent));
exponent = F32ToBf16Truncate(fexponent);
return out;
}
template <>
EIGEN_STRONG_INLINE Packet4bf pldexp(const Packet4bf& a, const Packet4bf& exponent) {
return F32ToBf16(pldexp<Packet4f>(Bf16ToF32(a), Bf16ToF32(exponent)));
}
//---------- double ----------
#if EIGEN_ARCH_ARM64
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_DOUBLE(Packet2d)
#endif
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_MATH_FUNCTIONS_NEON_H