blob: 3f64f872d6f7da2538c7e42bd1916eb635533bb6 [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_PACKET_MATH_BF16_NEON_H
#define EIGEN_PACKET_MATH_BF16_NEON_H
// IWYU pragma: private
#include "../../InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
// TODO: Add support for native armv8.6-a bfloat16_t
// TODO: Guard if we have native bfloat16 support
typedef eigen_packet_wrapper<uint16x4_t, 19> Packet4bf;
using Packet8bf = eigen_packet_wrapper<uint16x8_t, 20>;
template <>
struct is_arithmetic<Packet4bf> : std::true_type {};
template <>
struct is_arithmetic<Packet8bf> : std::true_type {};
// Packet8bf is the full packet: every bfloat16 op widens to float, and the float work of two
// Packet4f halves overlaps where one Packet4bf's chain would leave the pipes idle.
template <>
struct packet_traits<bfloat16> : default_packet_traits {
using type = Packet8bf;
using half = Packet4bf;
enum {
Vectorizable = 1,
AlignedOnScalar = 1,
size = 8,
HasCmp = 1,
HasAdd = 1,
HasSub = 1,
HasShift = 1,
HasMul = 1,
HasNegate = 1,
HasAbs = 1,
HasArg = 0,
HasAbsDiff = 1,
HasMin = 1,
HasMax = 1,
HasConj = 1,
HasSetLinear = 1,
HasDiv = 1,
HasSin = EIGEN_FAST_MATH,
HasCos = EIGEN_FAST_MATH,
HasLog = 1,
HasExp = 1,
HasSqrt = 1,
HasRsqrt = 1,
HasTanh = EIGEN_FAST_MATH,
HasErf = EIGEN_FAST_MATH,
HasBessel = 0, // Issues with accuracy.
HasNdtri = 0
};
};
template <>
struct unpacket_traits<Packet4bf> : neon_unpacket_default<Packet4bf, bfloat16> {};
template <>
struct unpacket_traits<Packet8bf> : neon_unpacket_default<Packet8bf, bfloat16> {
using half = Packet4bf;
};
namespace detail {
template <>
EIGEN_ALWAYS_INLINE void zip_in_place<Packet4bf>(Packet4bf& p1, Packet4bf& p2) {
const uint16x4x2_t tmp = vzip_u16(p1, p2);
p1 = tmp.val[0];
p2 = tmp.val[1];
}
template <>
EIGEN_ALWAYS_INLINE void zip_in_place<Packet8bf>(Packet8bf& p1, Packet8bf& p2) {
const uint16x8x2_t tmp = vzipq_u16(p1, p2);
p1 = tmp.val[0];
p2 = tmp.val[1];
}
} // namespace detail
// Round to nearest even on the bfloat16 boundary. With lsb the lowest bit kept, the scalar
// formula is (p + 0x7fff + lsb) >> 16, and RADDHN computes (p + d + 0x8000) >> 16, so d = lsb - 1,
// that is -1 where lsb is clear. Written as an add and RSHRN, the add stays a separate op in
// loops with GCC and clang on both targets. The caller guarantees that every NaN lane has a zero low
// half, which the FPU's NaN propagation does for bfloat16 operands: the rounding then leaves the
// NaN's high half alone. A NaN with a nonzero low half can instead round to +-inf or carry into a
// zero.
EIGEN_STRONG_INLINE Packet4bf F32ToBf16KeepNaN(const Packet4f& p) {
Packet4ui bits = vreinterpretq_u32_f32(p);
Packet4ui round_down = vceqq_u32(vandq_u32(bits, vdupq_n_u32(0x10000)), vdupq_n_u32(0));
return vraddhn_u32(bits, round_down);
}
// F32ToBf16KeepNaN for any float: NaN lanes become the bfloat16 NaN 0x7fc0, as on AVX. The scalar
// conversion keeps the NaN's sign; doing that here costs an op on the critical path of every round
// trip, 25% of a cast loop and 6% of exp on a Cortex-A72, for a bit that carries no value.
// |p| <= inf is false only for NaN. The NaN lanes are selected after narrowing, so the rounding
// reads the raw bits. Selecting first costs clang an extra AND and MOV.
EIGEN_STRONG_INLINE Packet4bf F32ToBf16(const Packet4f& p) {
uint16x4_t not_nan = vmovn_u32(vcageq_f32(pinf<Packet4f>(), p));
return vbsl_u16(not_nan, F32ToBf16KeepNaN(p), vdup_n_u16(0x7fc0));
}
// Discard the low 16 bits of each float. Only valid when every lane already holds an exact
// bfloat16 value, in which case this agrees with F32ToBf16 but skips its rounding and NaN
// canonicalization. pmin/pmax qualify: they return one of their operands bit for bit.
EIGEN_STRONG_INLINE Packet4bf F32ToBf16Truncate(const Packet4f& p) { return vshrn_n_u32(vreinterpretq_u32_f32(p), 16); }
EIGEN_STRONG_INLINE Packet4f Bf16ToF32(const Packet4bf& p) {
return Packet4f(vreinterpretq_f32_u32(vshlq_n_u32(vmovl_u16(p), 16)));
}
EIGEN_STRONG_INLINE Packet4bf F32MaskToBf16Mask(const Packet4f& p) { return vmovn_u32(vreinterpretq_u32_f32(p)); }
// Packet8bf conversions work on two Packet4f halves, lanes 0-3 and 4-7, with the same rounding as
// the Packet4bf ones.
EIGEN_STRONG_INLINE Packet4f Bf16ToF32Low(const Packet8bf& p) {
return vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(p), 16));
}
EIGEN_STRONG_INLINE Packet4f Bf16ToF32High(const Packet8bf& p) {
#if EIGEN_ARCH_ARM64
return vreinterpretq_f32_u32(vshll_high_n_u16(p, 16));
#else
return vreinterpretq_f32_u32(vshll_n_u16(vget_high_u16(p), 16));
#endif
}
// (v + 0x8000) >> 16 on each lane of lo and hi.
EIGEN_STRONG_INLINE Packet8bf RoundingNarrowBf16(const Packet4ui& lo, const Packet4ui& hi) {
#if EIGEN_ARCH_ARM64
return vrshrn_high_n_u32(vrshrn_n_u32(lo, 16), hi, 16);
#else
return vcombine_u16(vrshrn_n_u32(lo, 16), vrshrn_n_u32(hi, 16));
#endif
}
EIGEN_STRONG_INLINE Packet8bf F32ToBf16KeepNaN(const Packet4f& lo, const Packet4f& hi) {
Packet4ui lo_bits = vreinterpretq_u32_f32(lo);
Packet4ui hi_bits = vreinterpretq_u32_f32(hi);
Packet4ui lo_round_down = vceqq_u32(vandq_u32(lo_bits, vdupq_n_u32(0x10000)), vdupq_n_u32(0));
Packet4ui hi_round_down = vceqq_u32(vandq_u32(hi_bits, vdupq_n_u32(0x10000)), vdupq_n_u32(0));
#if EIGEN_ARCH_ARM64
return vraddhn_high_u32(vraddhn_u32(lo_bits, lo_round_down), hi_bits, hi_round_down);
#else
return vcombine_u16(vraddhn_u32(lo_bits, lo_round_down), vraddhn_u32(hi_bits, hi_round_down));
#endif
}
// As the Packet4bf F32ToBf16, with one UZP1 narrowing both NaN masks.
EIGEN_STRONG_INLINE Packet8bf F32ToBf16(const Packet4f& lo, const Packet4f& hi) {
Packet4f inf = pinf<Packet4f>();
uint16x8_t not_nan =
vuzpq_u16(vreinterpretq_u16_u32(vcageq_f32(inf, lo)), vreinterpretq_u16_u32(vcageq_f32(inf, hi))).val[0];
return vbslq_u16(not_nan, F32ToBf16KeepNaN(lo, hi), vdupq_n_u16(0x7fc0));
}
// The high and low 16 bits of every float: one UZP2 or UZP1 on AArch64, one VUZP on ARMv7.
EIGEN_STRONG_INLINE Packet8bf F32ToBf16Truncate(const Packet4f& lo, const Packet4f& hi) {
return vuzpq_u16(vreinterpretq_u16_f32(lo), vreinterpretq_u16_f32(hi)).val[1];
}
EIGEN_STRONG_INLINE Packet8bf F32MaskToBf16Mask(const Packet4f& lo, const Packet4f& hi) {
return vuzpq_u16(vreinterpretq_u16_f32(lo), vreinterpretq_u16_f32(hi)).val[0];
}
template <>
EIGEN_STRONG_INLINE Packet4bf pset1<Packet4bf>(const bfloat16& from) {
return Packet4bf(pset1<Packet4us>(from.value));
}
template <>
EIGEN_STRONG_INLINE bfloat16 pfirst<Packet4bf>(const Packet4bf& from) {
return bfloat16_impl::raw_uint16_to_bfloat16(static_cast<uint16_t>(pfirst<Packet4us>(Packet4us(from))));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pload<Packet4bf>(const bfloat16* from) {
return Packet4bf(
pload<Packet4us>(reinterpret_cast<const uint16_t*>(assume_aligned<unpacket_traits<Packet4bf>::alignment>(from))));
}
template <>
EIGEN_STRONG_INLINE Packet4bf ploadu<Packet4bf>(const bfloat16* from) {
return Packet4bf(ploadu<Packet4us>(reinterpret_cast<const uint16_t*>(from)));
}
template <>
EIGEN_STRONG_INLINE void pstore<bfloat16>(bfloat16* to, const Packet4bf& from) {
EIGEN_DEBUG_ALIGNED_STORE vst1_u16(
reinterpret_cast<uint16_t*>(assume_aligned<unpacket_traits<Packet4bf>::alignment>(to)), from);
}
template <>
EIGEN_STRONG_INLINE void pstoreu<bfloat16>(bfloat16* to, const Packet4bf& from) {
EIGEN_DEBUG_UNALIGNED_STORE vst1_u16(reinterpret_cast<uint16_t*>(to), from);
}
template <>
EIGEN_STRONG_INLINE Packet4bf ploaddup<Packet4bf>(const bfloat16* from) {
return Packet4bf(ploaddup<Packet4us>(reinterpret_cast<const uint16_t*>(from)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pabs(const Packet4bf& a) {
return Packet4bf(vand_u16(a, vdup_n_u16(0x7fff)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmin<PropagateNumbers, Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmin<PropagateNumbers, Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmin<PropagateNaN, Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmin<PropagateNaN, Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmin<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmin<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmax<PropagateNumbers, Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmax<PropagateNumbers, Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmax<PropagateNaN, Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmax<PropagateNaN, Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmax<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16Truncate(pmax<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf plset<Packet4bf>(const bfloat16& a) {
return F32ToBf16(plset<Packet4f>(static_cast<float>(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf por(const Packet4bf& a, const Packet4bf& b) {
return Packet4bf(por<Packet4us>(Packet4us(a), Packet4us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pxor(const Packet4bf& a, const Packet4bf& b) {
return Packet4bf(pxor<Packet4us>(Packet4us(a), Packet4us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pand(const Packet4bf& a, const Packet4bf& b) {
return Packet4bf(pand<Packet4us>(Packet4us(a), Packet4us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pandnot(const Packet4bf& a, const Packet4bf& b) {
return Packet4bf(pandnot<Packet4us>(Packet4us(a), Packet4us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pnot(const Packet4bf& a) {
return Packet4bf(pnot<Packet4us>(Packet4us(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf ptrue(const Packet4bf& /*a*/) {
return Packet4bf(vdup_n_u16(0xffff));
}
template <>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet4bf pselect(const Packet4bf& mask, const Packet4bf& a, const Packet4bf& b) {
return Packet4bf(pselect<Packet4us>(Packet4us(mask), Packet4us(a), Packet4us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf print<Packet4bf>(const Packet4bf& a) {
return F32ToBf16Truncate(print<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pfloor<Packet4bf>(const Packet4bf& a) {
return F32ToBf16Truncate(pfloor<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pceil<Packet4bf>(const Packet4bf& a) {
return F32ToBf16Truncate(pceil<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pround<Packet4bf>(const Packet4bf& a) {
return F32ToBf16Truncate(pround<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf ptrunc<Packet4bf>(const Packet4bf& a) {
return F32ToBf16Truncate(ptrunc<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf padd<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16KeepNaN(padd<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf psub<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16KeepNaN(psub<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmul<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16KeepNaN(pmul<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmadd<Packet4bf>(const Packet4bf& a, const Packet4bf& b, const Packet4bf& c) {
return F32ToBf16KeepNaN(pmadd<Packet4f>(Bf16ToF32(a), Bf16ToF32(b), Bf16ToF32(c)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pmsub<Packet4bf>(const Packet4bf& a, const Packet4bf& b, const Packet4bf& c) {
return F32ToBf16KeepNaN(pmsub<Packet4f>(Bf16ToF32(a), Bf16ToF32(b), Bf16ToF32(c)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pnmadd<Packet4bf>(const Packet4bf& a, const Packet4bf& b, const Packet4bf& c) {
return F32ToBf16KeepNaN(pnmadd<Packet4f>(Bf16ToF32(a), Bf16ToF32(b), Bf16ToF32(c)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pnmsub<Packet4bf>(const Packet4bf& a, const Packet4bf& b, const Packet4bf& c) {
return F32ToBf16KeepNaN(pnmsub<Packet4f>(Bf16ToF32(a), Bf16ToF32(b), Bf16ToF32(c)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pdiv<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16KeepNaN(pdiv<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pgather<bfloat16, Packet4bf>(const bfloat16* from, Index stride) {
return Packet4bf(pgather<uint16_t, Packet4us>(reinterpret_cast<const uint16_t*>(from), stride));
}
template <>
EIGEN_STRONG_INLINE void pscatter<bfloat16, Packet4bf>(bfloat16* to, const Packet4bf& from, Index stride) {
pscatter<uint16_t, Packet4us>(reinterpret_cast<uint16_t*>(to), Packet4us(from), stride);
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux<Packet4bf>(const Packet4bf& a) {
return static_cast<bfloat16>(predux<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_max<Packet4bf>(const Packet4bf& a) {
return bfloat16_impl::exact_float_to_bfloat16(predux_max<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_min<Packet4bf>(const Packet4bf& a) {
return bfloat16_impl::exact_float_to_bfloat16(predux_min<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_mul<Packet4bf>(const Packet4bf& a) {
return static_cast<bfloat16>(predux_mul<Packet4f>(Bf16ToF32(a)));
}
template <>
EIGEN_STRONG_INLINE bool predux_any(const Packet4bf& a) {
return vget_lane_u64(vreinterpret_u64_u16(Packet4us(a)), 0) != 0;
}
template <>
EIGEN_STRONG_INLINE Packet4bf preverse<Packet4bf>(const Packet4bf& a) {
return Packet4bf(preverse<Packet4us>(Packet4us(a)));
}
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<Packet4bf, 4>& kernel) {
detail::ptranspose_impl(kernel);
}
template <>
EIGEN_STRONG_INLINE Packet4bf pabsdiff<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32ToBf16KeepNaN(pabsdiff<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcmp_eq<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32MaskToBf16Mask(pcmp_eq<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcmp_lt<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32MaskToBf16Mask(pcmp_lt<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcmp_lt_or_nan<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32MaskToBf16Mask(pcmp_lt_or_nan<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
// Classify on the raw bits, as the scalar isinf/isnan/isfinite do: |a| ==, >, < 0x7f80.
template <>
EIGEN_STRONG_INLINE Packet4bf pisinf<Packet4bf>(const Packet4bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vceq_u16(vand_u16(a, vdup_n_u16(kAbsMask)), vdup_n_u16(kInf));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pisnan<Packet4bf>(const Packet4bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vcgt_u16(vand_u16(a, vdup_n_u16(kAbsMask)), vdup_n_u16(kInf));
}
// Compare encoded lanes: widening bf16 subnormals to float loses them under DAZ/FZ.
template <>
EIGEN_STRONG_INLINE Packet4bf psign<Packet4bf>(const Packet4bf& a) {
const uint16x4_t abs_mask = vdup_n_u16(0x7fff);
const uint16x4_t is_nan = vcgt_u16(vand_u16(a, abs_mask), vdup_n_u16(0x7f80));
const uint16x4_t keep = vorr_u16(is_nan, vdup_n_u16(0x8000));
return vand_u16(vbsl_u16(keep, a, vdup_n_u16(0x3f80)), vtst_u16(a, abs_mask));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pisfinite<Packet4bf>(const Packet4bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vclt_u16(vand_u16(a, vdup_n_u16(kAbsMask)), vdup_n_u16(kInf));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcmp_le<Packet4bf>(const Packet4bf& a, const Packet4bf& b) {
return F32MaskToBf16Mask(pcmp_le<Packet4f>(Bf16ToF32(a), Bf16ToF32(b)));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pnegate<Packet4bf>(const Packet4bf& a) {
return Packet4bf(pxor<Packet4us>(Packet4us(a), pset1<Packet4us>(static_cast<uint16_t>(0x8000))));
}
//---------- Packet8bf ----------
template <>
EIGEN_STRONG_INLINE Packet8bf pset1<Packet8bf>(const bfloat16& from) {
return Packet8bf(pset1<Packet8us>(from.value));
}
template <>
EIGEN_STRONG_INLINE bfloat16 pfirst<Packet8bf>(const Packet8bf& from) {
return bfloat16_impl::raw_uint16_to_bfloat16(static_cast<uint16_t>(pfirst<Packet8us>(Packet8us(from))));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pload<Packet8bf>(const bfloat16* from) {
return Packet8bf(
pload<Packet8us>(reinterpret_cast<const uint16_t*>(assume_aligned<unpacket_traits<Packet8bf>::alignment>(from))));
}
template <>
EIGEN_STRONG_INLINE Packet8bf ploadu<Packet8bf>(const bfloat16* from) {
return Packet8bf(ploadu<Packet8us>(reinterpret_cast<const uint16_t*>(from)));
}
template <>
EIGEN_STRONG_INLINE void pstore<bfloat16>(bfloat16* to, const Packet8bf& from) {
EIGEN_DEBUG_ALIGNED_STORE vst1q_u16(
reinterpret_cast<uint16_t*>(assume_aligned<unpacket_traits<Packet8bf>::alignment>(to)), from);
}
template <>
EIGEN_STRONG_INLINE void pstoreu<bfloat16>(bfloat16* to, const Packet8bf& from) {
EIGEN_DEBUG_UNALIGNED_STORE vst1q_u16(reinterpret_cast<uint16_t*>(to), from);
}
template <>
EIGEN_STRONG_INLINE Packet8bf ploaddup<Packet8bf>(const bfloat16* from) {
return Packet8bf(ploaddup<Packet8us>(reinterpret_cast<const uint16_t*>(from)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf ploadquad<Packet8bf>(const bfloat16* from) {
return Packet8bf(ploadquad<Packet8us>(reinterpret_cast<const uint16_t*>(from)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pabs(const Packet8bf& a) {
return Packet8bf(vandq_u16(a, vdupq_n_u16(0x7fff)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmin<PropagateNumbers, Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmin<PropagateNumbers, Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmin<PropagateNumbers, Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmin<PropagateNaN, Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmin<PropagateNaN, Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmin<PropagateNaN, Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmin<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmin<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmin<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmax<PropagateNumbers, Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmax<PropagateNumbers, Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmax<PropagateNumbers, Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmax<PropagateNaN, Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmax<PropagateNaN, Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmax<PropagateNaN, Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmax<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16Truncate(pmax<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmax<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf plset<Packet8bf>(const bfloat16& a) {
Packet4f lo = plset<Packet4f>(static_cast<float>(a));
return F32ToBf16(lo, padd(lo, pset1<Packet4f>(4.0f)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf por(const Packet8bf& a, const Packet8bf& b) {
return Packet8bf(por<Packet8us>(Packet8us(a), Packet8us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pxor(const Packet8bf& a, const Packet8bf& b) {
return Packet8bf(pxor<Packet8us>(Packet8us(a), Packet8us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pand(const Packet8bf& a, const Packet8bf& b) {
return Packet8bf(pand<Packet8us>(Packet8us(a), Packet8us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pandnot(const Packet8bf& a, const Packet8bf& b) {
return Packet8bf(pandnot<Packet8us>(Packet8us(a), Packet8us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pnot(const Packet8bf& a) {
return Packet8bf(pnot<Packet8us>(Packet8us(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf ptrue(const Packet8bf& /*a*/) {
return Packet8bf(vdupq_n_u16(0xffff));
}
template <>
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet8bf pselect(const Packet8bf& mask, const Packet8bf& a, const Packet8bf& b) {
return Packet8bf(pselect<Packet8us>(Packet8us(mask), Packet8us(a), Packet8us(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf print<Packet8bf>(const Packet8bf& a) {
return F32ToBf16Truncate(print<Packet4f>(Bf16ToF32Low(a)), print<Packet4f>(Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pfloor<Packet8bf>(const Packet8bf& a) {
return F32ToBf16Truncate(pfloor<Packet4f>(Bf16ToF32Low(a)), pfloor<Packet4f>(Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pceil<Packet8bf>(const Packet8bf& a) {
return F32ToBf16Truncate(pceil<Packet4f>(Bf16ToF32Low(a)), pceil<Packet4f>(Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pround<Packet8bf>(const Packet8bf& a) {
return F32ToBf16Truncate(pround<Packet4f>(Bf16ToF32Low(a)), pround<Packet4f>(Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf ptrunc<Packet8bf>(const Packet8bf& a) {
return F32ToBf16Truncate(ptrunc<Packet4f>(Bf16ToF32Low(a)), ptrunc<Packet4f>(Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf padd<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16KeepNaN(padd<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
padd<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf psub<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16KeepNaN(psub<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
psub<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmul<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16KeepNaN(pmul<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pmul<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmadd<Packet8bf>(const Packet8bf& a, const Packet8bf& b, const Packet8bf& c) {
return F32ToBf16KeepNaN(pmadd<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b), Bf16ToF32Low(c)),
pmadd<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b), Bf16ToF32High(c)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pmsub<Packet8bf>(const Packet8bf& a, const Packet8bf& b, const Packet8bf& c) {
return F32ToBf16KeepNaN(pmsub<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b), Bf16ToF32Low(c)),
pmsub<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b), Bf16ToF32High(c)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pnmadd<Packet8bf>(const Packet8bf& a, const Packet8bf& b, const Packet8bf& c) {
return F32ToBf16KeepNaN(pnmadd<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b), Bf16ToF32Low(c)),
pnmadd<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b), Bf16ToF32High(c)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pnmsub<Packet8bf>(const Packet8bf& a, const Packet8bf& b, const Packet8bf& c) {
return F32ToBf16KeepNaN(pnmsub<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b), Bf16ToF32Low(c)),
pnmsub<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b), Bf16ToF32High(c)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pdiv<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16KeepNaN(pdiv<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pdiv<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pgather<bfloat16, Packet8bf>(const bfloat16* from, Index stride) {
return Packet8bf(pgather<uint16_t, Packet8us>(reinterpret_cast<const uint16_t*>(from), stride));
}
template <>
EIGEN_STRONG_INLINE void pscatter<bfloat16, Packet8bf>(bfloat16* to, const Packet8bf& from, Index stride) {
pscatter<uint16_t, Packet8us>(reinterpret_cast<uint16_t*>(to), Packet8us(from), stride);
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux<Packet8bf>(const Packet8bf& a) {
return static_cast<bfloat16>(predux<Packet4f>(padd<Packet4f>(Bf16ToF32Low(a), Bf16ToF32High(a))));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_max<Packet8bf>(const Packet8bf& a) {
return bfloat16_impl::exact_float_to_bfloat16(
predux_max<Packet4f>(pmax<Packet4f>(Bf16ToF32Low(a), Bf16ToF32High(a))));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_min<Packet8bf>(const Packet8bf& a) {
return bfloat16_impl::exact_float_to_bfloat16(
predux_min<Packet4f>(pmin<Packet4f>(Bf16ToF32Low(a), Bf16ToF32High(a))));
}
template <>
EIGEN_STRONG_INLINE bfloat16 predux_mul<Packet8bf>(const Packet8bf& a) {
return static_cast<bfloat16>(predux_mul<Packet4f>(pmul<Packet4f>(Bf16ToF32Low(a), Bf16ToF32High(a))));
}
template <>
EIGEN_STRONG_INLINE Packet4bf predux_half<Packet8bf>(const Packet8bf& a) {
return F32ToBf16KeepNaN(padd<Packet4f>(Bf16ToF32Low(a), Bf16ToF32High(a)));
}
template <>
EIGEN_STRONG_INLINE bool predux_any(const Packet8bf& a) {
return predux_any(Packet8us(a));
}
template <>
EIGEN_STRONG_INLINE Packet8bf preverse<Packet8bf>(const Packet8bf& a) {
return Packet8bf(preverse<Packet8us>(Packet8us(a)));
}
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<Packet8bf, 4>& kernel) {
detail::ptranspose_impl(kernel);
}
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<Packet8bf, 8>& kernel) {
detail::ptranspose_impl(kernel);
}
template <>
EIGEN_STRONG_INLINE Packet8bf pabsdiff<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32ToBf16KeepNaN(pabsdiff<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pabsdiff<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pcmp_eq<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32MaskToBf16Mask(pcmp_eq<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pcmp_eq<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pcmp_lt<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32MaskToBf16Mask(pcmp_lt<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pcmp_lt<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pcmp_lt_or_nan<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
// Negate a >= b once on the narrowed mask rather than on each float half.
return vmvnq_u16(F32MaskToBf16Mask(pcmp_le<Packet4f>(Bf16ToF32Low(b), Bf16ToF32Low(a)),
pcmp_le<Packet4f>(Bf16ToF32High(b), Bf16ToF32High(a))));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pcmp_le<Packet8bf>(const Packet8bf& a, const Packet8bf& b) {
return F32MaskToBf16Mask(pcmp_le<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(b)),
pcmp_le<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(b)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pisinf<Packet8bf>(const Packet8bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vceqq_u16(vandq_u16(a, vdupq_n_u16(kAbsMask)), vdupq_n_u16(kInf));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pisnan<Packet8bf>(const Packet8bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vcgtq_u16(vandq_u16(a, vdupq_n_u16(kAbsMask)), vdupq_n_u16(kInf));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pisfinite<Packet8bf>(const Packet8bf& a) {
constexpr uint16_t kInf = ((1 << 8) - 1) << 7;
constexpr uint16_t kAbsMask = (1 << 15) - 1;
return vcltq_u16(vandq_u16(a, vdupq_n_u16(kAbsMask)), vdupq_n_u16(kInf));
}
template <>
EIGEN_STRONG_INLINE Packet8bf psign<Packet8bf>(const Packet8bf& a) {
uint16x8_t abs_mask = vdupq_n_u16(0x7fff);
uint16x8_t is_nan = vcgtq_u16(vandq_u16(a, abs_mask), vdupq_n_u16(0x7f80));
uint16x8_t keep = vorrq_u16(is_nan, vdupq_n_u16(0x8000));
return vandq_u16(vbslq_u16(keep, a, vdupq_n_u16(0x3f80)), vtstq_u16(a, abs_mask));
}
template <>
EIGEN_STRONG_INLINE Packet8bf pnegate<Packet8bf>(const Packet8bf& a) {
return Packet8bf(pxor<Packet8us>(Packet8us(a), pset1<Packet8us>(static_cast<uint16_t>(0x8000))));
}
// psqrt and prsqrt have their own kernels below.
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_BF16(Packet4f, Packet4bf)
EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_BF16_WITH(BF16_PACKET_FUNCTION_HALVES, Packet4f, Packet8bf)
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)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf psqrt<Packet8bf>(const Packet8bf& a) {
// psqrt<Packet4bf> on each float half, narrowed and selected once over all 8 lanes.
Packet4f x_lo = Bf16ToF32Low(a);
Packet4f x_hi = Bf16ToF32High(a);
Packet4f r_lo = vrsqrteq_f32(x_lo);
Packet4f r_hi = vrsqrteq_f32(x_hi);
Packet4f y_lo = pmul(x_lo, r_lo);
Packet4f y_hi = pmul(x_hi, r_hi);
y_lo = pmul(y_lo, vrsqrtsq_f32(y_lo, r_lo));
y_hi = pmul(y_hi, vrsqrtsq_f32(y_hi, r_hi));
Packet8bf ybf = RoundingNarrowBf16(vreinterpretq_u32_f32(y_lo), vreinterpretq_u32_f32(y_hi));
uint16x8_t u = vaddq_u16(F32ToBf16Truncate(r_lo, r_hi), vdupq_n_u16(0x80));
uint16x8_t step_lane = vtstq_u16(u, vdupq_n_u16(0x7f7f));
return vbslq_u16(step_lane, ybf, vandq_u16(a, vdupq_n_u16(0xff80)));
}
template <>
EIGEN_STRONG_INLINE Packet8bf prsqrt<Packet8bf>(const Packet8bf& a) {
// prsqrt<Packet4bf> on each float half.
Packet4f x_lo = Bf16ToF32Low(a);
Packet4f x_hi = Bf16ToF32High(a);
Packet4f r_lo = vrsqrteq_f32(x_lo);
Packet4f r_hi = vrsqrteq_f32(x_hi);
#if EIGEN_ARCH_ARM64
Packet4f r1_lo = pmul(r_lo, vabsq_f32(vrsqrtsq_f32(vmulxq_f32(x_lo, r_lo), r_lo)));
Packet4f r1_hi = pmul(r_hi, vabsq_f32(vrsqrtsq_f32(vmulxq_f32(x_hi, r_hi), r_hi)));
return RoundingNarrowBf16(vreinterpretq_u32_f32(r1_lo), vreinterpretq_u32_f32(r1_hi));
#else
Packet4f r1_lo = pmul(r_lo, vrsqrtsq_f32(pmul(x_lo, r_lo), r_lo));
Packet4f r1_hi = pmul(r_hi, vrsqrtsq_f32(pmul(x_hi, r_hi), r_hi));
Packet8bf refined = RoundingNarrowBf16(vreinterpretq_u32_f32(r1_lo), vreinterpretq_u32_f32(r1_hi));
Packet8bf raw = F32ToBf16Truncate(r_lo, r_hi);
uint16x8_t special = vcgeq_u16(vsubq_u16(a, vdupq_n_u16(0x80)), vdupq_n_u16(0x7f00));
return vbslq_u16(special, raw, refined);
#endif
}
template <>
EIGEN_STRONG_INLINE Packet8bf pfrexp(const Packet8bf& a, Packet8bf& exponent) {
Packet4f fexponent_lo;
Packet4f fexponent_hi;
Packet4f lo = pfrexp<Packet4f>(Bf16ToF32Low(a), fexponent_lo);
Packet4f hi = pfrexp<Packet4f>(Bf16ToF32High(a), fexponent_hi);
exponent = F32ToBf16Truncate(fexponent_lo, fexponent_hi);
return F32ToBf16Truncate(lo, hi);
}
template <>
EIGEN_STRONG_INLINE Packet8bf pldexp(const Packet8bf& a, const Packet8bf& exponent) {
return F32ToBf16(pldexp<Packet4f>(Bf16ToF32Low(a), Bf16ToF32Low(exponent)),
pldexp<Packet4f>(Bf16ToF32High(a), Bf16ToF32High(exponent)));
}
template <>
struct type_casting_traits<bfloat16, float> : vectorized_type_casting_traits<bfloat16, float> {};
template <>
struct type_casting_traits<float, bfloat16> : vectorized_type_casting_traits<float, bfloat16> {};
template <>
EIGEN_STRONG_INLINE Packet4f pcast<Packet4bf, Packet4f>(const Packet4bf& a) {
return Bf16ToF32(a);
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcast<Packet4f, Packet4bf>(const Packet4f& a) {
return F32ToBf16(a);
}
template <>
EIGEN_STRONG_INLINE Packet4f pcast<Packet8bf, Packet4f>(const Packet8bf& a) {
return Bf16ToF32Low(a);
}
template <>
EIGEN_STRONG_INLINE Packet8bf pcast<Packet4f, Packet8bf>(const Packet4f& a, const Packet4f& b) {
return F32ToBf16(a, b);
}
template <>
EIGEN_STRONG_INLINE Packet2f pcast<Packet4bf, Packet2f>(const Packet4bf& a) {
return vget_low_f32(Bf16ToF32(a));
}
template <>
EIGEN_STRONG_INLINE Packet4bf pcast<Packet2f, Packet4bf>(const Packet2f& a, const Packet2f& b) {
return F32ToBf16(vcombine_f32(a, b));
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_PACKET_MATH_BF16_NEON_H