Crafter.Math/interfaces/Crafter.Math-VectorF16.cppm

1670 lines
96 KiB
Text
Raw Normal View History

//SPDX-License-Identifier: LGPL-3.0-only
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
module;
#ifdef __x86_64
#include <immintrin.h>
#endif
#if defined(__aarch64__) && defined(__ARM_NEON)
#include <arm_neon.h>
#endif
export module Crafter.Math:VectorF16;
import std;
import :Common;
// A real VectorF16 struct is provided only when _Float16 is available AND we
// are on an x86_64 build with AVX512FP16 or an AArch64 NEON build with
// ARMv8.2-FP16 (intrinsic impls below), or on a target with neither x86_64 nor
// NEON (scalar fallback further below). On x86_64 without AVX512FP16, on
// AArch64 NEON without FP16 or anywhere without _Float16, Crafter.Math:Basic
// aliases VectorF16 to VectorF32 instead.
#ifdef __AVX512FP16__
namespace Crafter {
export template <std::uint8_t Len, std::uint8_t Packing>
struct VectorF16 : public VectorBase<Len, Packing, _Float16> {
template <std::uint8_t Len2, std::uint8_t Packing2>
friend struct VectorF16;
constexpr VectorF16() = default;
constexpr VectorF16(VectorBase<Len, Packing, _Float16>::VectorType v) {
this->v = v;
}
constexpr VectorF16(const _Float16* vB) {
Load(vB);
};
constexpr VectorF16(_Float16 val) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_set1_ph(val);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_set1_ph(val);
} else {
this->v = _mm512_set1_ph(val);
}
};
constexpr void Load(const _Float16* vB) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_loadu_ph(vB);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_loadu_ph(vB);
} else {
this->v = _mm512_loadu_ph(vB);
}
}
constexpr void Store(_Float16* vB) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
_mm_storeu_ph(vB, this->v);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
_mm256_storeu_ph(vB, this->v);
} else {
_mm512_storeu_ph(vB, this->v);
}
}
template<typename T>
constexpr std::array<_Float16, VectorBase<Len, Packing, _Float16>::AlignmentElement> Store() const {
std::array<_Float16, VectorBase<Len, Packing, _Float16>::AlignmentElement> returnArray;
Store(returnArray.data());
return returnArray;
}
template <std::uint8_t BLen, std::uint8_t BPacking>
constexpr operator VectorF16<BLen, BPacking>() const {
if constexpr (Len == BLen) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<BLen, BPacking>(_mm256_castph256_ph128(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<BLen, BPacking>(_mm512_castph512_ph128(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<BLen, BPacking>(_mm512_castph512_ph256(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<BLen, BPacking>(_mm256_castph128_ph256(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h>) {
return VectorF16<BLen, BPacking>(_mm512_castph128_ph512(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h> && std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h>) {
return VectorF16<BLen, BPacking>(_mm512_castph256_ph512(this->v));
} else {
return VectorF16<BLen, BPacking>(this->v);
}
} else if constexpr (BLen <= Len) {
return this->template ExtractLo<BLen>();
} else {
if constexpr(std::is_same_v<typename VectorBase<BLen, BPacking, _Float16>::VectorType, __m128h>) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
constexpr std::array<std::uint8_t, VectorBase<Len, Packing, _Float16>::Alignment> shuffleMask = VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi8<BLen>();
__m128i shuffleVec = _mm_loadu_epi8(shuffleMask.data());
return VectorF16<BLen, BPacking>(_mm_castsi128_ph(_mm_shuffle_epi8(_mm_castph_si128(this->v), shuffleVec)));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
constexpr std::array<std::uint16_t, VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask =VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m256i permIdx = _mm256_loadu_epi16(permMask.data());
__m256i result = _mm256_permutexvar_epi16(permIdx, _mm_castph_si256(this->v));
return VectorF16<BLen, BPacking>(_mm_castsi128_ph(_mm256_castsi256_si128(result)));
} else {
constexpr std::array<std::uint16_t, VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask = VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
__m512i result = _mm512_permutexvar_epi16(permIdx, _mm512_castph_si512(this->v));
return VectorF16<BLen, BPacking>(_mm_castsi128_ph(_mm512_castsi512_si128(result)));
}
} else if constexpr(std::is_same_v<typename VectorBase<BLen, BPacking, _Float16>::VectorType, __m256h>) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m256i permIdx = _mm256_loadu_epi16(permMask.data());
__m256i result = _mm256_permutexvar_epi16(permIdx, _mm256_castsi128_si256(_mm_castph_si128(this->v)));
return VectorF16<BLen, BPacking>(_mm256_castsi256_ph(result));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m256i permIdx = _mm256_loadu_epi16(permMask.data());
__m256i result = _mm256_permutexvar_epi16(permIdx, _mm256_castph_si256(this->v));
return VectorF16<BLen, BPacking>(_mm256_castsi256_ph(result));
} else {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m256i permIdx = _mm512_loadu_epi16(permMask.data());
__m256i result = _mm512_permutexvar_epi16(permIdx, _mm512_castsi512_si256(_mm512_castph_si512(this->v)));
return VectorF16<BLen, BPacking>(_mm256_castsi256_ph(result));
}
} else {
if constexpr(std::is_same_v<typename VectorBase<BLen, BPacking, _Float16>::VectorType, __m128h>) {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
__m512i result = _mm512_permutexvar_epi16(permIdx, _mm512_castsi128_si512(_mm_castph_si128(this->v)));
return VectorF16<BLen, BPacking>(_mm512_castsi512_ph(result));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
__m512i result = _mm512_permutexvar_epi16(permIdx, _mm512_castsi256_si512(_mm256_castph_si256(this->v)));
return VectorF16<BLen, BPacking>(_mm512_castsi512_ph(result));
} else {
constexpr std::array<std::uint16_t,VectorBase<BLen, Packing, _Float16>::AlignmentElement> permMask = VectorBase<BLen, Packing, _Float16>::template GetExtractLoMaskEpi16<BLen>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
__m512i result = _mm512_permutexvar_epi16(permIdx, _mm512_castph_si512(this->v));
return VectorF16<BLen, BPacking>(_mm512_castsi512_ph(result));
}
}
}
}
constexpr VectorF16<Len, Packing> operator+(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_add_ph(this->v, b.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_add_ph(this->v, b.v));
} else {
return VectorF16<Len, Packing>(_mm512_add_ph(this->v, b.v));
}
}
constexpr VectorF16<Len, Packing> operator-(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_sub_ph(this->v, b.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_sub_ph(this->v, b.v));
} else {
return VectorF16<Len, Packing>(_mm512_sub_ph(this->v, b.v));
}
}
constexpr VectorF16<Len, Packing> operator*(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_mul_ph(this->v, b.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_mul_ph(this->v, b.v));
} else {
return VectorF16<Len, Packing>(_mm512_mul_ph(this->v, b.v));
}
}
constexpr VectorF16<Len, Packing> operator/(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_div_ph(this->v, b.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_div_ph(this->v, b.v));
} else {
return VectorF16<Len, Packing>(_mm512_div_ph(this->v, b.v));
}
}
constexpr void operator+=(VectorF16<Len, Packing> b) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_add_ph(this->v, b.v);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_add_ph(this->v, b.v);
} else {
this->v = _mm512_add_ph(this->v, b.v);
}
}
constexpr void operator-=(VectorF16<Len, Packing> b) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_sub_ph(this->v, b.v);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_sub_ph(this->v, b.v);
} else {
this->v = _mm512_sub_ph(this->v, b.v);
}
}
constexpr void operator*=(VectorF16<Len, Packing> b) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_mul_ph(this->v, b.v);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_mul_ph(this->v, b.v);
} else {
this->v = _mm512_mul_ph(this->v, b.v);
}
}
constexpr void operator/=(VectorF16<Len, Packing> b) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
this->v = _mm_div_ph(this->v, b.v);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
this->v = _mm256_div_ph(this->v, b.v);
} else {
this->v = _mm512_div_ph(this->v, b.v);
}
}
constexpr VectorF16<Len, Packing> operator+(_Float16 b) {
VectorF16<Len, Packing> vB(b);
return *this + vB;
}
constexpr VectorF16<Len, Packing> operator-(_Float16 b) {
VectorF16<Len, Packing> vB(b);
return *this - vB;
}
constexpr VectorF16<Len, Packing> operator*(_Float16 b) {
VectorF16<Len, Packing> vB(b);
return *this * vB;
}
constexpr VectorF16<Len, Packing> operator/(_Float16 b) {
VectorF16<Len, Packing> vB(b);
return *this / vB;
}
constexpr void operator+=(_Float16 b) {
VectorF16<Len, Packing> vB(b);
*this += vB;
}
constexpr void operator-=(_Float16 b) {
VectorF16<Len, Packing> vB(b);
*this -= vB;
}
constexpr void operator*=(_Float16 b) {
VectorF16<Len, Packing> vB(b);
*this *= vB;
}
constexpr void operator/=(_Float16 b) {
VectorF16<Len, Packing> vB(b);
*this /= vB;
}
constexpr VectorF16<Len, Packing> operator-(){
return Negate<VectorBase<Len, Packing, _Float16>::GetAllTrue()>();
}
constexpr bool operator==(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return _mm_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) == 255;
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return _mm256_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) == 65535;
} else {
return _mm512_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) == 4294967295;
}
}
constexpr bool operator!=(VectorF16<Len, Packing> b) const {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return _mm_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) != 255;
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return _mm256_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) != 65535;
} else {
return _mm512_cmp_ph_mask(this->v, b.v, _CMP_EQ_OQ) != 4294967295;
}
}
template<std::uint32_t ExtractLen>
constexpr VectorF16<ExtractLen, Packing> ExtractLo() const {
if constexpr(Packing > 1) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
constexpr std::array<std::uint8_t,VectorBase<Len, Packing, _Float16>::Alignment> shuffleMask = VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi8<ExtractLen>();
__m128i shuffleVec = _mm_loadu_epi8(shuffleMask.data());
return VectorF16<ExtractLen, Packing>(_mm_castsi128_ph(_mm_shuffle_epi8(_mm_castph_si128(this->v), shuffleVec)));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
constexpr std::array<std::uint16_t,VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask = VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi16<ExtractLen>();
__m256i permIdx = _mm256_loadu_epi16(permMask.data());
__m256i result = _mm256_permutexvar_epi16(permIdx, _mm256_castph_si256(this->v));
if constexpr(std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<ExtractLen, Packing>(_mm256_castph256_ph128(_mm256_castsi256_ph(result)));
} else {
return VectorF16<ExtractLen, Packing>(_mm256_castsi256_ph(result));
}
} else {
constexpr std::array<std::uint16_t, VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask = VectorBase<Len, Packing, _Float16>::template GetExtractLoMaskEpi16<ExtractLen>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
__m512i result = _mm512_permutexvar_epi16(permIdx, _mm512_castph_si512(this->v));
if constexpr(std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<ExtractLen, Packing>(_mm512_castph512_ph128(_mm512_castsi512_ph(result)));
} else if constexpr(std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<ExtractLen, Packing>(_mm512_castph512_ph256(_mm512_castsi512_ph(result)));
} else {
return VectorF16<ExtractLen, Packing>(_mm512_castsi512_ph(result));
}
}
} else {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h> && std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<ExtractLen, Packing>(_mm256_castph256_ph128(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h> && std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<ExtractLen, Packing>(_mm512_castph512_ph128(this->v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m512h> && std::is_same_v<typename VectorBase<ExtractLen, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<ExtractLen, Packing>(_mm512_castph512_ph256(this->v));
} else {
return VectorF16<ExtractLen, Packing>(this->v);
}
}
}
constexpr VectorF16<Len, Packing> Cos() {
if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m256 wide = _mm256_cvtph_ps(_mm_castph_si128(this->v));
wide = VectorBase<Len, Packing, _Float16>::cos_f32x8(wide);
return VectorF16<Len, Packing>(
_mm_castsi128_ph(_mm256_cvtps_ph(wide, _MM_FROUND_TO_NEAREST_INT)));
} else if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m512 wide = _mm512_cvtph_ps(_mm256_castph_si256(this->v));
wide = VectorBase<Len, Packing, _Float16>::cos_f32x16(wide);
return VectorF16<Len, Packing>(
_mm256_castsi256_ph(_mm512_cvtps_ph(wide, _MM_FROUND_TO_NEAREST_INT)));
} else {
__m256i lo = _mm512_castsi512_si256(_mm512_castph_si512(this->v));
__m256i hi = _mm512_extracti64x4_epi64(_mm512_castph_si512(this->v), 1);
__m512i cosLo =VectorBase<Len, Packing, _Float16>::cos_f32x16(_mm512_cvtph_ps(lo));
__m512i cosHi =VectorBase<Len, Packing, _Float16>::cos_f32x16(_mm512_cvtph_ps(hi));
__m256i lo_ph = _mm512_cvtps_ph(cosLo, _MM_FROUND_TO_NEAREST_INT);
__m256i hi_ph = _mm512_cvtps_ph(cosHi, _MM_FROUND_TO_NEAREST_INT);
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_inserti64x4(_mm512_castsi256_si512(lo_ph), hi_ph, 1)));
}
}
constexpr VectorF16<Len, Packing> Sin() {
if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m256 wide = _mm256_cvtph_ps(_mm_castph_si128(this->v));
wide = VectorBase<Len, Packing, _Float16>::sin_f32x8(wide);
return VectorF16<Len, Packing>(_mm_castsi128_ph(_mm256_cvtps_ph(wide, _MM_FROUND_TO_NEAREST_INT)));
} else if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m512 wide = _mm512_cvtph_ps(_mm256_castph_si256(this->v));
wide = VectorBase<Len, Packing, _Float16>::sin_f32x16(wide);
return VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm512_cvtps_ph(wide, _MM_FROUND_TO_NEAREST_INT)));
} else {
__m256i lo = _mm512_castsi512_si256(_mm512_castph_si512(this->v));
__m256i hi = _mm512_extracti64x4_epi64(_mm512_castph_si512(this->v), 1);
__m512i loSin = VectorBase<Len, Packing, _Float16>::sin_f32x16(_mm512_cvtph_ps(lo));
__m512i hiSin = VectorBase<Len, Packing, _Float16>::sin_f32x16(_mm512_cvtph_ps(hi));
__m256i lo_ph = _mm512_cvtps_ph(loSin, _MM_FROUND_TO_NEAREST_INT);
__m256i hi_ph = _mm512_cvtps_ph(hiSin, _MM_FROUND_TO_NEAREST_INT);
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_inserti64x4(_mm512_castsi256_si512(lo_ph), hi_ph, 1)));
}
}
std::tuple<VectorF16<Len, Packing>, VectorF16<Len, Packing>> SinCos() {
if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m256 wide = _mm256_cvtph_ps(_mm_castph_si128(this->v));
__m256 s, c;
VectorBase<Len, Packing, _Float16>::sincos_f32x8(wide, s, c);
return {
VectorF16<Len, Packing>(_mm_castsi128_ph(_mm256_cvtps_ph(s, _MM_FROUND_TO_NEAREST_INT))),
VectorF16<Len, Packing>(_mm_castsi128_ph(_mm256_cvtps_ph(c, _MM_FROUND_TO_NEAREST_INT)))
};
} else if constexpr (std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m512 wide = _mm512_cvtph_ps(_mm256_castph_si256(this->v));
__m512 s, c;
VectorBase<Len, Packing, _Float16>::sincos_f32x16(wide, s, c);
return {
VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm512_cvtps_ph(s, _MM_FROUND_TO_NEAREST_INT))),
VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm512_cvtps_ph(c, _MM_FROUND_TO_NEAREST_INT)))
};
} else {
__m256i lo = _mm512_castsi512_si256(_mm512_castph_si512(this->v));
__m256i hi = _mm512_extracti64x4_epi64(_mm512_castph_si512(this->v), 1);
__m512 s_lo, c_lo, s_hi, c_hi;
VectorBase<Len, Packing, _Float16>::sincos_f32x16(_mm512_cvtph_ps(lo), s_lo, c_lo);
VectorBase<Len, Packing, _Float16>::sincos_f32x16(_mm512_cvtph_ps(hi), s_hi, c_hi);
auto pack = [](__m256i lo_ph, __m256i hi_ph) {
return _mm512_castsi512_ph(_mm512_inserti64x4(_mm512_castsi256_si512(lo_ph), hi_ph, 1));
};
return {
VectorF16<Len, Packing>(pack(_mm512_cvtps_ph(s_lo, _MM_FROUND_TO_NEAREST_INT), _mm512_cvtps_ph(s_hi, _MM_FROUND_TO_NEAREST_INT))),
VectorF16<Len, Packing>(pack( _mm512_cvtps_ph(c_lo, _MM_FROUND_TO_NEAREST_INT), _mm512_cvtps_ph(c_hi, _MM_FROUND_TO_NEAREST_INT)))
};
}
}
template <std::array<bool, Len> values>
constexpr VectorF16<Len, Packing> Negate() {
std::array<_Float16, VectorBase<Len, Packing, _Float16>::AlignmentElement> mask = VectorBase<Len, Packing, _Float16>::template GetNegateMask<values>();
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_castsi128_ph(_mm_xor_si128(_mm_castph_si128(this->v), _mm_loadu_epi16(mask.data()))));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm256_xor_si256(_mm256_castph_si256(this->v), _mm256_loadu_epi16(mask.data()))));
} else {
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_xor_si512(_mm512_castph_si512(this->v), _mm512_loadu_epi16(mask.data()))));
}
}
static constexpr VectorF16<Len, Packing> MulitplyAdd(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> add) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_fmadd_ph(a.v, b.v, add.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_fmadd_ph(a.v, b.v, add.v));
} else {
return VectorF16<Len, Packing>(_mm512_fmadd_ph(a.v, b.v, add.v));
}
}
static constexpr VectorF16<Len, Packing> MulitplySub(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> sub) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_fmsub_ph(a.v, b.v, sub.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_fmsub_ph(a.v, b.v, sub.v));
} else {
return VectorF16<Len, Packing>(_mm512_fmsub_ph(a.v, b.v, sub.v));
}
}
2026-10-31 06:52:22 +01:00
// Through Shuffle, as VectorF32::Cross is: it checks whether every lane
// move stays inside its 128-bit lane and takes a full permute when one
// does not. This used to call _mm*_shuffle_epi8 directly, which
// shuffles bytes per 128-bit lane only, so in a 256- or 512-bit
// register a 3-vector whose halves straddle a lane boundary (the third
// of every eight for Packing 8, say) came out wrong.
constexpr static VectorF16<Len, Packing> Cross(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) requires(Len == 3) {
2026-10-31 06:52:22 +01:00
VectorF16<Len, Packing> row1 = a.template Shuffle<{{1,2,0}}>();
VectorF16<Len, Packing> row4 = b.template Shuffle<{{1,2,0}}>();
2026-10-31 06:52:22 +01:00
VectorF16<Len, Packing> row3 = a.template Shuffle<{{2,0,1}}>();
VectorF16<Len, Packing> row2 = b.template Shuffle<{{2,0,1}}>();
2026-10-31 06:52:22 +01:00
VectorF16<Len, Packing> result = row3 * row4;
return VectorF16<Len, Packing>::MulitplySub(row1, row2, result);
}
constexpr static _Float16 Dot(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) requires(Packing == 1) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m128h mul = _mm_mul_ph(a.v, b.v);
return _mm_reduce_add_ph(mul);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
static_assert(std::is_same_v<decltype(a.v), typename VectorBase<Len, Packing, _Float16>::VectorType>, "a.v is NOT VectorBase<Len, Packing, _Float16>::VectorType");
__m256h mul = _mm256_mul_ph(a.v, b.v);
return _mm256_reduce_add_ph(mul);
} else {
__m512h mul = _mm512_mul_ph(a.v, b.v);
return _mm512_reduce_add_ph(mul);
}
}
template <const std::array<std::uint8_t, Len> ShuffleValues>
constexpr VectorF16<Len, Packing> Shuffle() {
if constexpr(VectorBase<Len, Packing, _Float16>::template CheckEpi32Shuffle<ShuffleValues>()) {
constexpr std::uint8_t imm = VectorBase<Len, Packing, _Float16>::template GetShuffleMaskEpi32<ShuffleValues>();
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<Len, Packing>(_mm_castsi128_ph(_mm_shuffle_epi32(_mm_castph_si128(this->v), imm)));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm256_shuffle_epi32(_mm256_castph_si256(this->v), imm)));
} else {
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_shuffle_epi32(_mm512_castph_si512(this->v), imm)));
}
} else if constexpr(VectorBase<Len, Packing, _Float16>::template CheckEpi8Shuffle<ShuffleValues>()){
constexpr std::array<std::uint8_t, VectorBase<Len, Packing, _Float16>::Alignment> shuffleMask = VectorBase<Len, Packing, _Float16>::template GetShuffleMaskEpi8<ShuffleValues>();
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m128i shuffleVec = _mm_loadu_epi8(shuffleMask.data());
return VectorF16<Len, Packing>(_mm_castsi128_ph(_mm_shuffle_epi8(_mm_castph_si128(this->v), shuffleVec)));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m256i shuffleVec = _mm256_loadu_epi8(shuffleMask.data());
return VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm512_castsi512_si256(_mm512_shuffle_epi8(_mm512_castsi256_si512(_mm256_castph_si256(this->v)), _mm512_castsi256_si512(shuffleVec)))));
} else {
__m512i shuffleVec = _mm512_loadu_epi8(shuffleMask.data());
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_shuffle_epi8(_mm512_castph_si512(this->v), shuffleVec)));
}
} else {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
constexpr std::array<std::uint8_t, VectorBase<Len, Packing, _Float16>::Alignment> shuffleMask = VectorBase<Len, Packing, _Float16>::template GetShuffleMaskEpi8<ShuffleValues>();
__m128i shuffleVec = _mm_loadu_epi8(shuffleMask.data());
return VectorF16<Len, Packing>(_mm_castsi128_ph(_mm_shuffle_epi8(_mm_castph_si128(this->v), shuffleVec)));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
constexpr std::array<std::uint16_t, VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask = VectorBase<Len, Packing, _Float16>::template GetPermuteMaskEpi16<ShuffleValues>();
__m256i permIdx = _mm256_loadu_epi16(permMask.data());
return VectorF16<Len, Packing>(_mm256_castsi256_ph(_mm256_permutexvar_epi16(permIdx, _mm256_castph_si256(this->v))));
} else {
constexpr std::array<std::uint16_t, VectorBase<Len, Packing, _Float16>::AlignmentElement> permMask = VectorBase<Len, Packing, _Float16>::template GetPermuteMaskEpi16<ShuffleValues>();
__m512i permIdx = _mm512_loadu_epi16(permMask.data());
return VectorF16<Len, Packing>(_mm512_castsi512_ph(_mm512_permutexvar_epi16(permIdx, _mm512_castph_si512(this->v))));
}
}
}
// Public variadic surface — one name per op, arity locked to BatchSize
// (or 2*BatchSize for Dot). Forwards to the *Pack helpers below which
// carry the SIMD bodies and per-(Len,Packing) requires clauses.
template <typename... Rest>
requires ((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Normalize(VectorF16<Len, Packing> first, Rest... rest) {
return NormalizePack(first, rest...);
}
template <typename... Rest>
requires ((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Length(VectorF16<Len, Packing> first, Rest... rest) {
return LengthPack(first, rest...);
}
template <typename... Rest>
requires ((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto LengthSq(VectorF16<Len, Packing> first, Rest... rest) {
return LengthSqPack(first, rest...);
}
template <typename... Rest>
requires ((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == 2 * VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Dot(VectorF16<Len, Packing> first, Rest... rest) {
return DotPack(first, rest...);
}
private:
constexpr static std::array<VectorF16<Len, Packing>, VectorBase<Len, Packing, _Float16>::BatchSize> NormalizePack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> C,
VectorF16<Len, Packing> E,
VectorF16<Len, Packing> G
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
VectorF16<1, 8> lenght = LengthNoShuffle(A, E, C, G);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1};
__m128h one = _mm_loadu_ph(oneArr);
VectorF16<8, 1> fLenght(_mm_div_ph(one, lenght.v));
VectorF16<8, 1> fLenghtA = fLenght.template Shuffle<{{0,0,0,0,1,1,1,1}}>();
VectorF16<8, 1> fLenghtC = fLenght.template Shuffle<{{2,2,2,2,3,3,3,3}}>();
VectorF16<8, 1> fLenghtE = fLenght.template Shuffle<{{4,4,4,4,5,5,5,5}}>();
VectorF16<8, 1> fLenghtG = fLenght.template Shuffle<{{6,6,6,6,7,7,7,7}}>();
return {
_mm_mul_ph(A.v, fLenghtA.v),
_mm_mul_ph(C.v, fLenghtC.v),
_mm_mul_ph(E.v, fLenghtE.v),
_mm_mul_ph(G.v, fLenghtG.v)
};
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
VectorF16<1, 16> lenght = LengthNoShuffle(A, E, C, G);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
__m256h one = _mm256_loadu_ph(oneArr);
VectorF16<16, 1> fLenght(_mm256_div_ph(one, lenght.v));
VectorF16<16, 1> fLenghtA = fLenght.template Shuffle<{{0,0,0,0,1,1,1,1,8,8,8,8,9,9,9,9}}>();
VectorF16<16, 1> fLenghtC = fLenght.template Shuffle<{{2,2,2,2,3,3,3,3,10,10,10,10,11,11,11,11}}>();
VectorF16<16, 1> fLenghtE = fLenght.template Shuffle<{{4,4,4,4,5,5,5,5,12,12,12,12,13,13,13,13}}>();
VectorF16<16, 1> fLenghtG = fLenght.template Shuffle<{{6,6,6,6,7,7,7,7,14,14,14,14,15,15,15,15}}>();
return {
_mm256_mul_ph(A.v, fLenghtA.v),
_mm256_mul_ph(C.v, fLenghtC.v),
_mm256_mul_ph(E.v, fLenghtE.v),
_mm256_mul_ph(G.v, fLenghtG.v)
};
} else {
VectorF16<1, 32> lenght = LengthNoShuffle(A, E, C, G);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
__m512h one = _mm512_loadu_ph(oneArr);
VectorF16<32, 1> fLenght(_mm512_div_ph(one, lenght.v));
VectorF16<32, 1> fLenght2(lenght.v);
VectorF16<32, 1> fLenghtA = fLenght.template Shuffle<{{0,0,0,0,1,1,1,1,8,8,8,8,9,9,9,9,16,16,16,16,17,17,17,17,24,24,24,24,25,25,25,25}}>();
VectorF16<32, 1> fLenghtC = fLenght.template Shuffle<{{2,2,2,2,3,3,3,3,10,10,10,10,11,11,11,11,18,18,18,18,19,19,19,19,26,26,26,26,27,27,27,27}}>();
VectorF16<32, 1> fLenghtE = fLenght.template Shuffle<{{4,4,4,4,5,5,5,5,12,12,12,12,13,13,13,13,20,20,20,20,21,21,21,21,28,28,28,28,29,29,29,29}}>();
VectorF16<32, 1> fLenghtG = fLenght.template Shuffle<{{6,6,6,6,7,7,7,7,14,14,14,14,15,15,15,15,22,22,22,22,23,23,23,23,30,30,30,30,31,31,31,31}}>();
return {
VectorF16<Len, Packing>(_mm512_mul_ph(A.v, fLenghtA.v)),
VectorF16<Len, Packing>(_mm512_mul_ph(C.v, fLenghtC.v)),
VectorF16<Len, Packing>(_mm512_mul_ph(E.v, fLenghtE.v)),
VectorF16<Len, Packing>(_mm512_mul_ph(G.v, fLenghtG.v)),
};
}
}
constexpr static std::array<VectorF16<Len, Packing>, VectorBase<Len, Packing, _Float16>::BatchSize> NormalizePack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> E
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
VectorF16<1, 8> lenght = LengthNoShuffle(A, E);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1};
__m128h one = _mm_loadu_ph(oneArr);
VectorF16<8, 1> fLenght(_mm_div_ph(one, lenght.v));
VectorF16<8, 1> fLenghtA = fLenght.template Shuffle<{{0,0,1,1,2,2,3,3}}>();
VectorF16<8, 1> fLenghtE = fLenght.template Shuffle<{{4,4,5,5,6,6,7,7}}>();
return {
_mm_mul_ph(A.v, fLenghtA.v),
_mm_mul_ph(E.v, fLenghtE.v),
};
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
VectorF16<1, 16> lenght = LengthNoShuffle(A, E);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
__m256h one = _mm256_loadu_ph(oneArr);
VectorF16<16, 1> fLenght(_mm256_div_ph(one, lenght.v));
VectorF16<16, 1> fLenghtA = fLenght.template Shuffle<{{0,0,1,1,2,2,3,3,8,8,9,9,10,10,11,11}}>();
VectorF16<16, 1> fLenghtE = fLenght.template Shuffle<{{4,4,5,5,6,6,7,7,12,12,13,13,14,14,15,15}}>();
return {
_mm256_mul_ph(A.v, fLenghtA.v),
_mm256_mul_ph(E.v, fLenghtE.v),
};
} else {
VectorF16<1, 32> lenght = LengthNoShuffle(A, E);
constexpr _Float16 oneArr[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
__m512h one = _mm512_loadu_ph(oneArr);
VectorF16<32, 1> fLenght(_mm512_div_ph(one, lenght.v));
VectorF16<32, 1> fLenghtA = fLenght.template Shuffle<{{0,0,1,1,2,2,3,3,8,8,9,9,10,10,11,11,16,16,17,17,18,18,19,19,24,24,25,25,26,26,27,27}}>();
VectorF16<32, 1> fLenghtE = fLenght.template Shuffle<{{4,4,5,5,6,6,7,7,12,12,13,13,14,14,15,15,20,20,21,21,22,22,23,23,28,28,29,29,30,30,31,31}}>();
return {
_mm512_mul_ph(A.v, fLenghtA.v),
_mm512_mul_ph(E.v, fLenghtE.v),
};
}
}
constexpr static VectorF16<1, Packing*4> LengthPack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> C,
VectorF16<Len, Packing> E,
VectorF16<Len, Packing> G
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
VectorF16<1, Packing*4> lenghtSq = LengthSqPack(A, C, E, G);
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<1, Packing*4>(_mm_sqrt_ph(lenghtSq.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<1, Packing*4>(_mm256_sqrt_ph(lenghtSq.v));
} else {
return VectorF16<1, Packing*4>(_mm512_sqrt_ph(lenghtSq.v));
}
}
constexpr static VectorF16<1, Packing*2> LengthPack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> E
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
VectorF16<1, Packing*2> lenghtSq = LengthSqPack(A, E);
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<1, Packing*2>(_mm_sqrt_ph(lenghtSq.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<1, Packing*2>(_mm256_sqrt_ph(lenghtSq.v));
} else {
return VectorF16<1, Packing*2>(_mm512_sqrt_ph(lenghtSq.v));
}
}
constexpr static VectorF16<1, Packing*4> LengthSqPack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> C,
VectorF16<Len, Packing> E,
VectorF16<Len, Packing> G
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
return DotPack(A, A, C, C, E, E, G, G);
}
constexpr static VectorF16<1, Packing*2> LengthSqPack(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> E
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
return DotPack(A, A, E, E);
}
constexpr static VectorF16<1, Packing*4> DotPack(
VectorF16<Len, Packing> A0, VectorF16<Len, Packing> A1,
VectorF16<Len, Packing> C0, VectorF16<Len, Packing> C1,
VectorF16<Len, Packing> E0, VectorF16<Len, Packing> E1,
VectorF16<Len, Packing> G0, VectorF16<Len, Packing> G1
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return DotNoShuffle(A0, A1, E0, E1, C0, C1, G0, G1);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
VectorF16<16, 1> vec(DotNoShuffle(A0, A1, C0, C1, E0, E1, G0, G1).v);
vec = vec.template Shuffle<{{
0,1,8,9,
4,5,12,13,
2,3,10,11,
6,7,14,15
}}>();
return vec.v;
} else {
VectorF16<32, 1> vec(DotNoShuffle(A0, A1, C0, C1, E0, E1, G0, G1).v);
vec = vec.template Shuffle<{{
0,1,8,9,
16,17,24,25,
4,5,12,13,
20,21,28,29,
2,3,10,11,
18,19,24,25,
6,7,14,15,
22,23,30,31
}}>();
return vec.v;
}
}
constexpr static VectorF16<1, Packing*2> DotPack(
VectorF16<Len, Packing> A0, VectorF16<Len, Packing> A1,
VectorF16<Len, Packing> E0, VectorF16<Len, Packing> E1
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return DotNoShuffle(A0, A1, E0, E1);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
VectorF16<16, 1> vec(DotNoShuffle(A0, A1, E0, E1).v);
vec = vec.template Shuffle<{{0,1,2,3,8,9,10,11,4,5,6,7,12,13,14,15}}>();
return vec.v;
} else {
VectorF16<32, 1> vec(DotNoShuffle(A0, A1, E0, E1).v);
vec = vec.template Shuffle<{{0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27,4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31}}>();
return vec.v;
}
}
private:
constexpr static VectorF16<1, Packing*4> LengthNoShuffle(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> C,
VectorF16<Len, Packing> E,
VectorF16<Len, Packing> G
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
VectorF16<1, Packing*4> lenghtSq = LengthSqNoShuffle(A, C, E, G);
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<1, Packing*4>(_mm_sqrt_ph(lenghtSq.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<1, Packing*4>(_mm256_sqrt_ph(lenghtSq.v));
} else {
return VectorF16<1, Packing*4>(_mm512_sqrt_ph(lenghtSq.v));
}
}
constexpr static VectorF16<1, Packing*2> LengthNoShuffle(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> E
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
VectorF16<1, Packing*2> lenghtSq = LengthSqNoShuffle(A, E);
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return VectorF16<1, Packing*2>(_mm_sqrt_ph(lenghtSq.v));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
return VectorF16<1, Packing*2>(_mm256_sqrt_ph(lenghtSq.v));
} else {
return VectorF16<1, Packing*2>(_mm512_sqrt_ph(lenghtSq.v));
}
}
constexpr static VectorF16<1, Packing*4> LengthSqNoShuffle(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> C,
VectorF16<Len, Packing> E,
VectorF16<Len, Packing> G
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
return DotNoShuffle(A, A, C, C, E, E, G, G);
}
constexpr static VectorF16<1, Packing*2> LengthSqNoShuffle(
VectorF16<Len, Packing> A,
VectorF16<Len, Packing> E
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
return DotNoShuffle(A, A, E, E);
}
constexpr static VectorF16<1, Packing*4> DotNoShuffle(
VectorF16<Len, Packing> A0, VectorF16<Len, Packing> A1,
VectorF16<Len, Packing> C0, VectorF16<Len, Packing> C1,
VectorF16<Len, Packing> E0, VectorF16<Len, Packing> E1,
VectorF16<Len, Packing> G0, VectorF16<Len, Packing> G1
) requires(Len == 4 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m128h mulA = _mm_mul_ph(A0.v, A1.v);
__m128h mulC = _mm_mul_ph(C0.v, C1.v);
__m128i row12Temp1 = _mm_unpacklo_epi16(_mm_castph_si128(mulA), _mm_castph_si128(mulC)); // A1 C1 A2 C2 A3 C3 A4 C4
__m128i row34Temp1 = _mm_unpackhi_epi16(_mm_castph_si128(mulA), _mm_castph_si128(mulC)); // B1 D1 B2 D2 B3 D3 B4 D4
__m128i row1TempTemp1 = row12Temp1;
__m128h mulE = _mm_mul_ph(E0.v, E1.v);
__m128h mulG = _mm_mul_ph(G0.v, G1.v);
__m128i row12Temp2 = _mm_unpacklo_epi16(_mm_castph_si128(mulE), _mm_castph_si128(mulG)); // E1 G1 E2 G2 E3 G3 E4 G4
__m128i row12Temp2Temp = row12Temp2;
__m128i row34Temp2 = _mm_unpackhi_epi16(_mm_castph_si128(mulE), _mm_castph_si128(mulG)); // F1 H1 F2 H2 F3 H3 F4 H4
row12Temp1 = _mm_unpacklo_epi16(row12Temp1, row12Temp2); // A1 E1 C1 G1 A2 E2 C2 G2
row12Temp2 = _mm_unpacklo_epi16(row34Temp1, row34Temp2); // B1 F1 D1 H1 B2 F2 D2 H2
row34Temp2 = _mm_unpackhi_epi16(row34Temp1, row34Temp2); // B3 F3 D3 H3 B4 F4 D4 H4
row34Temp1 = _mm_unpackhi_epi16(row1TempTemp1, row12Temp2Temp); // A3 E3 C3 G3 A4 E4 C4 G4
__m128h row1 = _mm_castsi128_ph(_mm_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 E1 F1 C1 D1 G1 H1
__m128h row2 = _mm_castsi128_ph(_mm_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 E2 F2 C2 D2 G2 H2
__m128h row3 = _mm_castsi128_ph(_mm_unpacklo_epi16(row34Temp1, row34Temp2));// A3 B3 E3 F3 C3 D3 G3 H3
__m128h row4 = _mm_castsi128_ph(_mm_unpackhi_epi16(row34Temp1, row34Temp2));// A4 B4 E4 F4 C4 D4 G4 H4
row1 = _mm_add_ph(row1, row2);
row1 = _mm_add_ph(row1, row3);
row1 = _mm_add_ph(row1, row4);
return row1;
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m256h mulA = _mm256_mul_ph(A0.v, A1.v);
__m256h mulC = _mm256_mul_ph(C0.v, C1.v);
__m256i row12Temp1 = _mm256_unpacklo_epi16(_mm256_castph_si256(mulA), _mm256_castph_si256(mulC)); // A1 C1 A2 C2 A3 C3 A4 C4
__m256i row34Temp1 = _mm256_unpackhi_epi16(_mm256_castph_si256(mulA), _mm256_castph_si256(mulC)); // B1 D1 B2 D2 B3 D3 B4 D4
__m256i row1TempTemp1 = row12Temp1;
__m256h mulE = _mm256_mul_ph(E0.v, E1.v);
__m256h mulG = _mm256_mul_ph(G0.v, G1.v);
__m256i row12Temp2 = _mm256_unpacklo_epi16(_mm256_castph_si256(mulE), _mm256_castph_si256(mulG)); // E1 G1 E2 G2 E3 G3 E4 G4
__m256i row12Temp2Temp = row12Temp2;
__m256i row34Temp2 = _mm256_unpackhi_epi16(_mm256_castph_si256(mulE), _mm256_castph_si256(mulG)); // F1 H1 F2 H2 F3 H3 F4 H4
row12Temp1 = _mm256_unpacklo_epi16(row12Temp1, row12Temp2); // A1 E1 C1 G1 A2 E2 C2 G2
row12Temp2 = _mm256_unpacklo_epi16(row34Temp1, row34Temp2); // B1 F1 D1 H1 B2 F2 D2 H2
row34Temp2 = _mm256_unpackhi_epi16(row34Temp1, row34Temp2); // B3 F3 D3 H3 B4 F4 D4 H4
row34Temp1 = _mm256_unpackhi_epi16(row1TempTemp1, row12Temp2Temp); // A3 E3 C3 G3 A4 E4 C4 G4
__m256h row1 = _mm256_castsi256_ph(_mm256_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 E1 F1 C1 D1 G1 H1
__m256h row2 = _mm256_castsi256_ph(_mm256_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 E2 F2 C2 D2 G2 H2
__m256h row3 = _mm256_castsi256_ph(_mm256_unpacklo_epi16(row34Temp1, row34Temp2));// A3 B3 E3 F3 C3 D3 G3 H3
__m256h row4 = _mm256_castsi256_ph(_mm256_unpackhi_epi16(row34Temp1, row34Temp2));// A4 B4 E4 F4 C4 D4 G4 H4
row1 = _mm256_add_ph(row1, row2);
row1 = _mm256_add_ph(row1, row3);
row1 = _mm256_add_ph(row1, row4);
return row1;
} else {
__m512h mulA = _mm512_mul_ph(A0.v, A1.v);
__m512h mulC = _mm512_mul_ph(C0.v, C1.v);
__m512i row12Temp1 = _mm512_unpacklo_epi16(_mm512_castph_si512(mulA), _mm512_castph_si512(mulC)); // A1 C1 A2 C2 A3 C3 A4 C4
__m512i row34Temp1 = _mm512_unpackhi_epi16(_mm512_castph_si512(mulA), _mm512_castph_si512(mulC)); // B1 D1 B2 D2 B3 D3 B4 D4
__m512i row1TempTemp1 = row12Temp1;
__m512h mulE = _mm512_mul_ph(E0.v, E1.v);
__m512h mulG = _mm512_mul_ph(G0.v, G1.v);
__m512i row12Temp2 = _mm512_unpacklo_epi16(_mm512_castph_si512(mulE), _mm512_castph_si512(mulG)); // E1 G1 E2 G2 E3 G3 E4 G4
__m512i row12Temp2Temp = row12Temp2;
__m512i row34Temp2 = _mm512_unpackhi_epi16(_mm512_castph_si512(mulE), _mm512_castph_si512(mulG)); // F1 H1 F2 H2 F3 H3 F4 H4
row12Temp1 = _mm512_unpacklo_epi16(row12Temp1, row12Temp2); // A1 E1 C1 G1 A2 E2 C2 G2
row12Temp2 = _mm512_unpacklo_epi16(row34Temp1, row34Temp2); // B1 F1 D1 H1 B2 F2 D2 H2
row34Temp2 = _mm512_unpackhi_epi16(row34Temp1, row34Temp2); // B3 F3 D3 H3 B4 F4 D4 H4
row34Temp1 = _mm512_unpackhi_epi16(row1TempTemp1, row12Temp2Temp); // A3 E3 C3 G3 A4 E4 C4 G4
__m512h row1 = _mm512_castsi512_ph(_mm512_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 E1 F1 C1 D1 G1 H1
__m512h row2 = _mm512_castsi512_ph(_mm512_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 E2 F2 C2 D2 G2 H2
__m512h row3 = _mm512_castsi512_ph(_mm512_unpacklo_epi16(row34Temp1, row34Temp2));// A3 B3 E3 F3 C3 D3 G3 H3
__m512h row4 = _mm512_castsi512_ph(_mm512_unpackhi_epi16(row34Temp1, row34Temp2));// A4 B4 E4 F4 C4 D4 G4 H4
row1 = _mm512_add_ph(row1, row2);
row1 = _mm512_add_ph(row1, row3);
row1 = _mm512_add_ph(row1, row4);
return row1;
}
}
constexpr static VectorF16<1, Packing*2> DotNoShuffle(
VectorF16<Len, Packing> A0, VectorF16<Len, Packing> A1,
VectorF16<Len, Packing> E0, VectorF16<Len, Packing> E1
) requires(Len == 2 && Packing*Len == VectorBase<Len, Packing, _Float16>::AlignmentElement) {
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
__m128h mulA = _mm_mul_ph(A0.v, A1.v);
__m128h mulE = _mm_mul_ph(E0.v, E1.v);
__m128i row12Temp1 = _mm_unpacklo_epi16(_mm_castph_si128(mulA), _mm_castph_si128(mulE)); // A1 E1 A2 E2 B1 F1 B2 F2
__m128i row12Temp2 = _mm_unpackhi_epi16(_mm_castph_si128(mulA), _mm_castph_si128(mulE)); // C1 G1 C2 G2 D1 H1 D2 H2
__m128i row12Temp1Temp = row12Temp1;
row12Temp1 = _mm_unpacklo_epi16(row12Temp1, row12Temp2); // A1 C1 E1 G1 A2 C2 E2 G2
row12Temp2 = _mm_unpackhi_epi16(row12Temp1Temp, row12Temp2); // B1 D1 F1 H1 B2 D2 F2 H2
__m128h row1 = _mm_castsi128_ph(_mm_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 C1 D1 E1 F1 G1 H1
__m128h row2 = _mm_castsi128_ph(_mm_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 C2 D2 E2 F2 G2 H2
return _mm_add_ph(row1, row2);
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
__m256h mulA = _mm256_mul_ph(A0.v, A1.v);
__m256h mulE = _mm256_mul_ph(E0.v, E1.v);
__m256i row12Temp1 = _mm256_unpacklo_epi16(_mm256_castph_si256(mulA), _mm256_castph_si256(mulE)); // A1 E1 A2 E2 B1 F1 B2 F2
__m256i row12Temp2 = _mm256_unpackhi_epi16(_mm256_castph_si256(mulA), _mm256_castph_si256(mulE)); // C1 G1 C2 G2 D1 H1 D2 H2
__m256i row12Temp1Temp = row12Temp1;
row12Temp1 = _mm256_unpacklo_epi16(row12Temp1, row12Temp2); // A1 C1 E1 G1 A2 C2 E2 G2
row12Temp2 = _mm256_unpackhi_epi16(row12Temp1Temp, row12Temp2); // B1 D1 F1 H1 B2 D2 F2 H2
__m256h row1 = _mm256_castsi256_ph(_mm256_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 C1 D1 E1 F1 G1 H1
__m256h row2 = _mm256_castsi256_ph(_mm256_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 C2 D2 E2 F2 G2 H2
__m256h result = _mm256_add_ph(row1, row2);
return result;
} else {
__m512h mulA = _mm512_mul_ph(A0.v, A1.v);
__m512h mulE = _mm512_mul_ph(E0.v, E1.v);
__m512i row12Temp1 = _mm512_unpacklo_epi16(_mm512_castph_si512(mulA), _mm512_castph_si512(mulE)); // A1 E1 A2 E2 B1 F1 B2 F2
__m512i row12Temp2 = _mm512_unpackhi_epi16(_mm512_castph_si512(mulA), _mm512_castph_si512(mulE)); // C1 G1 C2 G2 D1 H1 D2 H2
__m512i row12Temp1Temp = row12Temp1;
row12Temp1 = _mm512_unpacklo_epi16(row12Temp1, row12Temp2); // A1 C1 E1 G1 A2 C2 E2 G2
row12Temp2 = _mm512_unpackhi_epi16(row12Temp1Temp, row12Temp2); // B1 D1 F1 H1 B2 D2 F2 H2
__m512h row1 = _mm512_castsi512_ph(_mm512_unpacklo_epi16(row12Temp1, row12Temp2));// A1 B1 C1 D1 E1 F1 G1 H1
__m512h row2 = _mm512_castsi512_ph(_mm512_unpackhi_epi16(row12Temp1, row12Temp2));// A2 B2 C2 D2 E2 F2 G2 H2
__m512h result = _mm512_add_ph(row1, row2);
return result;
}
}
public:
template <std::array<bool, Len> ShuffleValues>
constexpr static VectorF16<Len, Packing> Blend(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) {
constexpr auto mask = VectorBase<Len, Packing, _Float16>::template GetBlendMaskEpi16<ShuffleValues>();
if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m128h>) {
return _mm_castsi128_ph(_mm_blend_epi16(_mm_castph_si128(a.v), _mm_castph_si128(b.v), mask));
} else if constexpr(std::is_same_v<typename VectorBase<Len, Packing, _Float16>::VectorType, __m256h>) {
#ifndef __AVX512BW__
#ifndef __AVX512VL__
static_assert(false, "No __AVX512BW__ and __AVX512VL__ support");
#endif
#endif
return _mm256_castsi256_ph(_mm256_mask_blend_epi16(mask, _mm256_castph_si256(a.v), _mm256_castph_si256(b.v)));
} else {
return _mm512_castsi512_ph(_mm512_mask_blend_epi16(mask, _mm512_castph_si512(a.v), _mm512_castph_si512(b.v)));
}
}
constexpr static VectorF16<Len, Packing> Rotate(VectorF16<3, Packing> v, VectorF16<4, Packing> q) requires(Len == 3) {
VectorF16<3, Packing> qv(q);
VectorF16<Len, Packing> t = Cross(qv, v) * _Float16(2);
return v + t * q.template Shuffle<{{3,3,3,3}}>() + Cross(qv, t);
}
2026-10-31 06:52:22 +01:00
// Through Rotate, like the other backends; see VectorF32's note.
constexpr static VectorF16<3, Packing> RotatePivot(VectorF16<3, Packing> v, VectorF16<4, Packing> q, VectorF16<3, Packing> pivot) requires(Len == 3) {
VectorF16<3, Packing> translated = v - pivot;
return Rotate(translated, q) + pivot;
}
constexpr static VectorF16<4, Packing> QuanternionFromEuler(VectorF16<3, Packing> EulerHalf) requires(Len == 4) {
std::tuple<VectorF16<3, Packing>, VectorF16<3, Packing>> sinCos = EulerHalf.SinCos();
VectorF16<4, Packing> sin = std::get<0>(sinCos);
VectorF16<4, Packing> cos = std::get<1>(sinCos);
VectorF16<4, Packing> row1 = cos.template Shuffle<{{0,0,0,0}}>();
row1 = Blend<{{0,1,1,1}}>(sin, row1);
VectorF16<4, Packing> row2 = cos.template Shuffle<{{1,1,1,1}}>();
row2 = Blend<{{1,0,1,1}}>(sin, row2);
row1 *= row2;
VectorF16<4, Packing> row3 = cos.template Shuffle<{{2,2,2,2}}>();
row3 = Blend<{{1,1,0,1}}>(sin, row3);
row1 *= row3;
VectorF16<4, Packing> row4 = sin.template Shuffle<{{0,0,0,0}}>();
row4 = Blend<{{0,1,1,1}}>(cos, row4);
VectorF16<4, Packing> row5 = sin.template Shuffle<{{1,1,1,1}}>();
row5 = Blend<{{1,0,1,1}}>(cos, row5);
row4 *= row5;
VectorF16<4, Packing> row6 = sin.template Shuffle<{{2,2,2,2}}>();
row6 = Blend<{{1,1,0,1}}>(cos, row6);
row6 = row6.template Negate<{{true,false,true,false}}>();
row1 = MulitplyAdd(row4, row6, row1);
return row1;
}
};
}
#elif defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
// AArch64 NEON with ARMv8.2-FP16 half-precision arithmetic. Built the same
// way as the NEON VectorF32: every vector is one 128-bit register, here a
// float16x8_t with eight lanes, and shuffles and blends are compile-time lane
// maps handed to __builtin_shufflevector.
namespace Crafter {
export template <std::uint8_t Len, std::uint8_t Packing>
struct VectorF16 : public VectorBase<Len, Packing, _Float16> {
template <std::uint8_t Len2, std::uint8_t Packing2>
friend struct VectorF16;
using Base = VectorBase<Len, Packing, _Float16>;
static constexpr std::uint8_t NElems = Base::AlignmentElement;
static_assert(NElems == 8, "NEON VectorF16 assumes 8-lane vectors");
private:
// Source lane for each of the eight output lanes: 0..7 pick from the
// first operand of Permute, 8..15 from the second. Every map names a
// real source lane for all eight outputs, padding included, so no
// lane of a result is ever undefined.
using LaneMap = std::array<int, 8>;
static constexpr LaneMap InPlace{0, 1, 2, 3, 4, 5, 6, 7};
template <LaneMap Lanes, typename Register>
static constexpr Register Permute(Register a, Register b) {
return __builtin_shufflevector(a, b, Lanes[0], Lanes[1], Lanes[2], Lanes[3], Lanes[4], Lanes[5], Lanes[6], Lanes[7]);
}
// Per-pack shuffle; padding lanes stay where they are.
template <std::array<std::uint8_t, Len> ShuffleValues>
static consteval LaneMap GetShuffleLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
lanes[p * Len + i] = p * Len + ShuffleValues[i];
return lanes;
}
// Per-pack blend: the second operand where the mask is set, the first
// elsewhere.
template <std::array<bool, Len> BlendValues>
static consteval LaneMap GetBlendLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
if (BlendValues[i]) lanes[p * Len + i] += 8;
return lanes;
}
// Moves every sub-vector to its slot in a (ToLen, ToPacking) layout,
// carrying over the components both layouts have. The remaining lanes
// are left as they were.
template <std::uint8_t ToLen, std::uint8_t ToPacking>
static consteval LaneMap GetResizeLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t p = 0; p < std::min(Packing, ToPacking); ++p)
for (std::uint8_t i = 0; i < std::min(Len, ToLen); ++i)
lanes[p * ToLen + i] = p * Len + i;
return lanes;
}
// Normalize keeps one scale per sub-vector in a VectorF16<1, N*Packing>;
// this spreads the scales of input `Index` over the Len lanes of each
// of its sub-vectors. Padding lanes repeat the last scale.
template <std::uint8_t Index>
static consteval LaneMap GetSpreadLanes() {
LaneMap lanes{};
for (std::uint8_t i = 0; i < 8; ++i)
lanes[i] = Index * Packing + std::min(i / Len, Packing - 1);
return lanes;
}
// QuanternionFromEuler factor row out of the sin (first operand) and
// cos (second operand) of a VectorF16<3, Packing>: in each quaternion,
// lane `Axis` takes the sin of that axis and the other three lanes
// its cos, or the other way around when `Swap` is set.
template <std::uint8_t Axis, bool Swap>
static consteval LaneMap GetEulerLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < 4; ++i)
lanes[p * 4 + i] = ((i == Axis) != Swap ? 0 : 8) + p * 3 + Axis;
return lanes;
}
// The batched reductions sum sub-vectors with faddp, which adds the
// adjacent lane pairs of two registers at once. That takes every
// sub-vector in a power-of-two slot of lanes: `Slot` lanes wide,
// `Slots` of them per register.
static constexpr std::uint8_t Slot = std::bit_ceil(Len);
static constexpr std::uint8_t Slots = 8 / Slot;
// Spreads the sub-vectors out to one slot each and zero-fills the rest
// (lane 8 is a zero from Permute's second operand). Power-of-two Len
// sits in its slots already, and whatever shares the register with it
// ends up in sums of its own.
static consteval LaneMap GetSlotLanes() {
LaneMap lanes{};
for (std::uint8_t i = 0; i < 8; ++i)
lanes[i] = (i / Slot < Packing && i % Slot < Len) ? (i / Slot) * Len + i % Slot : 8;
return lanes;
}
static constexpr float16x8_t ToSlots(float16x8_t m) {
if constexpr (Slot != Len) {
return Permute<GetSlotLanes()>(m, vdupq_n_f16(0));
} else {
return m;
}
}
// Once the faddp levels are done, a register holds the sums of `Slot`
// inputs, each in the leading Packing lanes of a group of Slots. This
// is the lane the i-th of those sums sits in.
static consteval int SumLane(std::uint8_t i) {
return (i / Packing) * Slots + i % Packing;
}
static consteval LaneMap GetSumLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t i = 0; i < Slot * Packing; ++i) lanes[i] = SumLane(i);
return lanes;
}
// Puts the first `Width` values of a, then the first `Width` of b, at
// the front. `Sums` says the values still sit where the faddp levels
// left them (see SumLane) instead of already being at the front.
template <std::uint8_t Width, bool Sums>
static consteval LaneMap GetJoinLanes() {
LaneMap lanes = InPlace;
for (std::uint8_t i = 0; i < Width; ++i) {
lanes[i] = Sums ? SumLane(i) : i;
lanes[Width + i] = 8 + lanes[i];
}
return lanes;
}
// Concatenates the sums of the first `Count` registers, `Width` of
// them per register, halving the register count per step.
template <std::uint8_t Count, std::uint8_t Width, bool Sums>
static constexpr float16x8_t Join(std::array<float16x8_t, Base::BatchSize> r) {
if constexpr (Count > 1) {
for (std::uint8_t i = 0; i < Count / 2; ++i)
r[i] = Permute<GetJoinLanes<Width, Sums>()>(r[2 * i], r[2 * i + 1]);
return Join<Count / 2, 2 * Width, false>(r);
} else if constexpr (Sums) {
return Permute<GetSumLanes()>(r[0], r[0]);
} else {
return r[0];
}
}
// Collapses every sub-vector of the BatchSize registers into the sum
// of its components: result lane i*Packing+p is the sum of lanes
// [p*Len, p*Len+Len) of m[i]. Each faddp level halves the slot width
// and packs two registers into one, so log2(Slot) levels leave plain
// sums that Join then lines up.
static constexpr float16x8_t SumPacks(std::array<float16x8_t, Base::BatchSize> m) {
constexpr std::uint8_t N = Base::BatchSize;
static_assert(N % Slot == 0);
for (float16x8_t& product : m) product = ToSlots(product);
std::uint8_t count = N;
for (std::uint8_t width = Slot; width > 1; width /= 2) {
count /= 2;
for (std::uint8_t i = 0; i < count; ++i) m[i] = vpaddq_f16(m[2 * i], m[2 * i + 1]);
}
return Join<N / Slot, Slot * Packing, true>(m);
}
public:
constexpr VectorF16() = default;
constexpr VectorF16(float16x8_t vv) { this->v = vv; }
constexpr VectorF16(const _Float16* vB) { Load(vB); }
constexpr VectorF16(_Float16 val) { this->v = vdupq_n_f16(val); }
// Through memcpy because arm_neon.h types its fp16 pointers as __fp16,
// which _Float16 may not alias.
constexpr void Load(const _Float16* vB) { std::memcpy(&this->v, vB, sizeof(this->v)); }
constexpr void Store(_Float16* vB) const { std::memcpy(vB, &this->v, sizeof(this->v)); }
template<typename T>
constexpr std::array<_Float16, NElems> Store() const {
std::array<_Float16, NElems> r;
Store(r.data());
return r;
}
template <std::uint8_t BLen, std::uint8_t BPacking>
constexpr operator VectorF16<BLen, BPacking>() const {
return VectorF16<BLen, BPacking>(Permute<GetResizeLanes<BLen, BPacking>()>(this->v, this->v));
}
constexpr VectorF16<Len, Packing> operator+(VectorF16<Len, Packing> b) const { return VectorF16<Len, Packing>(vaddq_f16(this->v, b.v)); }
constexpr VectorF16<Len, Packing> operator-(VectorF16<Len, Packing> b) const { return VectorF16<Len, Packing>(vsubq_f16(this->v, b.v)); }
constexpr VectorF16<Len, Packing> operator*(VectorF16<Len, Packing> b) const { return VectorF16<Len, Packing>(vmulq_f16(this->v, b.v)); }
constexpr VectorF16<Len, Packing> operator/(VectorF16<Len, Packing> b) const { return VectorF16<Len, Packing>(vdivq_f16(this->v, b.v)); }
constexpr void operator+=(VectorF16<Len, Packing> b) { this->v = vaddq_f16(this->v, b.v); }
constexpr void operator-=(VectorF16<Len, Packing> b) { this->v = vsubq_f16(this->v, b.v); }
constexpr void operator*=(VectorF16<Len, Packing> b) { this->v = vmulq_f16(this->v, b.v); }
constexpr void operator/=(VectorF16<Len, Packing> b) { this->v = vdivq_f16(this->v, b.v); }
constexpr VectorF16<Len, Packing> operator+(_Float16 b) const { return *this + VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator-(_Float16 b) const { return *this - VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator*(_Float16 b) const { return *this * VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator/(_Float16 b) const { return *this / VectorF16<Len, Packing>(b); }
constexpr void operator+=(_Float16 b) { *this += VectorF16<Len, Packing>(b); }
constexpr void operator-=(_Float16 b) { *this -= VectorF16<Len, Packing>(b); }
constexpr void operator*=(_Float16 b) { *this *= VectorF16<Len, Packing>(b); }
constexpr void operator/=(_Float16 b) { *this /= VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator-() const { return VectorF16<Len, Packing>(vnegq_f16(this->v)); }
// Only the Len*Packing lanes in use take part; padding lanes can hold
// anything. Narrowing the compare result to a byte per lane puts all
// eight in one 64-bit integer, where the lanes in use are its low
// bytes.
constexpr bool operator==(VectorF16<Len, Packing> b) const {
constexpr std::uint64_t used = ~0ull >> (64 - 8 * Len * Packing);
std::uint64_t equal = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(vceqq_f16(this->v, b.v))), 0);
return (equal & used) == used;
}
constexpr bool operator!=(VectorF16<Len, Packing> b) const { return !(*this == b); }
template<std::uint32_t ExtractLen>
constexpr VectorF16<ExtractLen, Packing> ExtractLo() const {
return VectorF16<ExtractLen, Packing>(Permute<GetResizeLanes<ExtractLen, Packing>()>(this->v, this->v));
}
// Sin and cos run in single precision: the register widens to fp32,
// goes through the shared kernels and narrows back. The upper half is
// skipped when it is all padding.
constexpr std::tuple<VectorF16<Len, Packing>, VectorF16<Len, Packing>> SinCos() const {
float32x4_t sinLo, cosLo;
Base::sincos_f32x4(vcvt_f32_f16(vget_low_f16(this->v)), sinLo, cosLo);
float32x4_t sinHi = sinLo, cosHi = cosLo;
if constexpr (Len * Packing > 4) {
Base::sincos_f32x4(vcvt_f32_f16(vget_high_f16(this->v)), sinHi, cosHi);
}
return {
VectorF16<Len, Packing>(vcombine_f16(vcvt_f16_f32(sinLo), vcvt_f16_f32(sinHi))),
VectorF16<Len, Packing>(vcombine_f16(vcvt_f16_f32(cosLo), vcvt_f16_f32(cosHi)))
};
}
constexpr VectorF16<Len, Packing> Cos() const { return std::get<1>(SinCos()); }
constexpr VectorF16<Len, Packing> Sin() const { return std::get<0>(SinCos()); }
template <std::array<bool, Len> values>
constexpr VectorF16<Len, Packing> Negate() const {
constexpr auto mask = []() {
std::array<std::uint16_t, NElems> m{};
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
m[p * Len + i] = values[i] ? 0x8000u : 0u;
return m;
}();
return VectorF16<Len, Packing>(vreinterpretq_f16_u16(veorq_u16(vreinterpretq_u16_f16(this->v), vld1q_u16(mask.data()))));
}
// vfmaq_f16(a, b, c) is a + b*c.
static constexpr VectorF16<Len, Packing> MulitplyAdd(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> add) {
return VectorF16<Len, Packing>(vfmaq_f16(add.v, a.v, b.v));
}
static constexpr VectorF16<Len, Packing> MulitplySub(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> sub) {
return VectorF16<Len, Packing>(vfmaq_f16(vnegq_f16(sub.v), a.v, b.v));
}
// a × b = (a * b.yzx - b * a.yzx).yzx, one shuffle less than building
// both yzx and zxy of each input.
constexpr static VectorF16<Len, Packing> Cross(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) requires(Len == 3) {
VectorF16<Len, Packing> aYzx = a.template Shuffle<{{1,2,0}}>();
VectorF16<Len, Packing> bYzx = b.template Shuffle<{{1,2,0}}>();
VectorF16<Len, Packing> zxy(vfmaq_f16(vmulq_f16(a.v, bYzx.v), vnegq_f16(b.v), aYzx.v));
return zxy.template Shuffle<{{1,2,0}}>();
}
constexpr static _Float16 Dot(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) requires(Packing == 1) {
float16x8_t sum = ToSlots(vmulq_f16(a.v, b.v));
for (std::uint8_t width = Slot; width > 1; width /= 2) sum = vpaddq_f16(sum, sum);
return vgetq_lane_f16(sum, 0);
}
template <const std::array<std::uint8_t, Len> ShuffleValues>
constexpr VectorF16<Len, Packing> Shuffle() const {
return VectorF16<Len, Packing>(Permute<GetShuffleLanes<ShuffleValues>()>(this->v, this->v));
}
template <std::array<bool, Len> ShuffleValues>
constexpr static VectorF16<Len, Packing> Blend(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) {
return VectorF16<Len, Packing>(Permute<GetBlendLanes<ShuffleValues>()>(a.v, b.v));
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto LengthSq(VectorF16<Len, Packing> first, Rest... rest) {
constexpr std::uint8_t N = VectorBase<Len, Packing, _Float16>::BatchSize;
std::array<VectorF16<Len, Packing>, N> args{ first, rest... };
std::array<float16x8_t, N> squares;
for (std::uint8_t i = 0; i < N; ++i) squares[i] = vmulq_f16(args[i].v, args[i].v);
return VectorF16<1, static_cast<std::uint8_t>(Packing * N)>(SumPacks(squares));
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Length(VectorF16<Len, Packing> first, Rest... rest) {
auto sq = LengthSq(first, rest...);
sq.v = vsqrtq_f16(sq.v);
return sq;
}
// Pairwise dot products across BatchSize pairs: Dot(a0, b0, a1, b1,
// ...) returns one lane per sub-vector pair.
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == 2 * VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Dot(VectorF16<Len, Packing> first, Rest... rest) {
constexpr std::uint8_t N = VectorBase<Len, Packing, _Float16>::BatchSize;
std::array<VectorF16<Len, Packing>, 2 * N> args{ first, rest... };
std::array<float16x8_t, N> products;
for (std::uint8_t i = 0; i < N; ++i) products[i] = vmulq_f16(args[2 * i].v, args[2 * i + 1].v);
return VectorF16<1, static_cast<std::uint8_t>(Packing * N)>(SumPacks(products));
}
// One sqrt and one divide cover the whole batch; each input then
// multiplies by its own lanes of the result.
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Normalize(VectorF16<Len, Packing> first, Rest... rest) {
constexpr std::uint8_t N = VectorBase<Len, Packing, _Float16>::BatchSize;
std::array<VectorF16<Len, Packing>, N> args{ first, rest... };
float16x8_t scale = vdivq_f16(vdupq_n_f16(1), Length(first, rest...).v);
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::array<VectorF16<Len, Packing>, N>{
VectorF16<Len, Packing>(vmulq_f16(args[Is].v, Permute<GetSpreadLanes<Is>()>(scale, scale)))...
};
}(std::make_index_sequence<N>{});
}
constexpr static VectorF16<Len, Packing> Rotate(VectorF16<3, Packing> v, VectorF16<4, Packing> q) requires(Len == 3) {
VectorF16<3, Packing> qv = q;
VectorF16<3, Packing> qw = q.template Shuffle<{{3,3,3,3}}>();
VectorF16<3, Packing> t = Cross(qv, v) * _Float16(2);
return MulitplyAdd(t, qw, v) + Cross(qv, t);
}
constexpr static VectorF16<3, Packing> RotatePivot(VectorF16<3, Packing> v, VectorF16<4, Packing> q, VectorF16<3, Packing> pivot) requires(Len == 3) {
VectorF16<3, Packing> translated = v - pivot;
return Rotate(translated, q) + pivot;
}
// With (roll, pitch, yaw) half angles in x, y, z:
// x = sx*cy*cz - cx*sy*sz y = cx*sy*cz + sx*cy*sz
// z = cx*cy*sz - sx*sy*cz w = cx*cy*cz + sx*sy*sz
// Each factor row below is one two-source shuffle of the sin and cos
// registers (see GetEulerLanes).
constexpr static VectorF16<4, Packing> QuanternionFromEuler(VectorF16<3, Packing> EulerHalf) requires(Len == 4) {
std::tuple<VectorF16<3, Packing>, VectorF16<3, Packing>> sinCos = EulerHalf.SinCos();
float16x8_t s = std::get<0>(sinCos).v;
float16x8_t c = std::get<1>(sinCos).v;
float16x8_t left = vmulq_f16(
vmulq_f16(
Permute<GetEulerLanes<0, false>()>(s, c), // sx cx cx cx
Permute<GetEulerLanes<1, false>()>(s, c)), // cy sy cy cy
Permute<GetEulerLanes<2, false>()>(s, c)); // cz cz sz cz
float16x8_t right = vmulq_f16(
Permute<GetEulerLanes<0, true>()>(s, c), // cx sx sx sx
Permute<GetEulerLanes<1, true>()>(s, c)); // sy cy sy sy
VectorF16<4, Packing> rightZ(Permute<GetEulerLanes<2, true>()>(s, c)); // sz sz cz sz
rightZ = rightZ.template Negate<{{true, false, true, false}}>();
return VectorF16<4, Packing>(vfmaq_f16(left, right, rightZ.v));
}
};
}
#elif !defined(__x86_64) && defined(__FLT16_MAX__) && !(defined(__aarch64__) && defined(__ARM_NEON))
// Scalar software fallback for non-x86_64 targets that still have _Float16.
namespace Crafter {
export template <std::uint8_t Len, std::uint8_t Packing>
struct VectorF16 : public VectorBase<Len, Packing, _Float16> {
template <std::uint8_t Len2, std::uint8_t Packing2>
friend struct VectorF16;
using Base = VectorBase<Len, Packing, _Float16>;
static constexpr std::uint8_t NElems = Base::AlignmentElement;
constexpr VectorF16() = default;
constexpr VectorF16(typename Base::VectorType vv) {
this->v = vv;
}
constexpr VectorF16(const _Float16* vB) { Load(vB); }
constexpr VectorF16(_Float16 val) {
for (std::uint8_t i = 0; i < NElems; ++i) this->v[i] = val;
}
constexpr void Load(const _Float16* vB) {
for (std::uint8_t i = 0; i < NElems; ++i) this->v[i] = vB[i];
}
constexpr void Store(_Float16* vB) const {
for (std::uint8_t i = 0; i < NElems; ++i) vB[i] = this->v[i];
}
template<typename T>
constexpr std::array<_Float16, NElems> Store() const {
std::array<_Float16, NElems> r{};
Store(r.data());
return r;
}
template <std::uint8_t BLen, std::uint8_t BPacking>
constexpr operator VectorF16<BLen, BPacking>() const {
VectorF16<BLen, BPacking> r;
const std::uint8_t copyLen = (BLen < Len) ? BLen : Len;
const std::uint8_t copyPack = (BPacking < Packing) ? BPacking : Packing;
for (std::uint8_t p = 0; p < copyPack; ++p)
for (std::uint8_t i = 0; i < copyLen; ++i)
r.v[p * BLen + i] = this->v[p * Len + i];
return r;
}
constexpr VectorF16<Len, Packing> operator+(VectorF16<Len, Packing> b) const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = this->v[i] + b.v[i];
return r;
}
constexpr VectorF16<Len, Packing> operator-(VectorF16<Len, Packing> b) const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = this->v[i] - b.v[i];
return r;
}
constexpr VectorF16<Len, Packing> operator*(VectorF16<Len, Packing> b) const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = this->v[i] * b.v[i];
return r;
}
constexpr VectorF16<Len, Packing> operator/(VectorF16<Len, Packing> b) const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = this->v[i] / b.v[i];
return r;
}
constexpr void operator+=(VectorF16<Len, Packing> b) { for (std::uint8_t i=0;i<NElems;++i) this->v[i] += b.v[i]; }
constexpr void operator-=(VectorF16<Len, Packing> b) { for (std::uint8_t i=0;i<NElems;++i) this->v[i] -= b.v[i]; }
constexpr void operator*=(VectorF16<Len, Packing> b) { for (std::uint8_t i=0;i<NElems;++i) this->v[i] *= b.v[i]; }
constexpr void operator/=(VectorF16<Len, Packing> b) { for (std::uint8_t i=0;i<NElems;++i) this->v[i] /= b.v[i]; }
constexpr VectorF16<Len, Packing> operator+(_Float16 b) const { return *this + VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator-(_Float16 b) const { return *this - VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator*(_Float16 b) const { return *this * VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator/(_Float16 b) const { return *this / VectorF16<Len, Packing>(b); }
constexpr void operator+=(_Float16 b) { *this += VectorF16<Len, Packing>(b); }
constexpr void operator-=(_Float16 b) { *this -= VectorF16<Len, Packing>(b); }
constexpr void operator*=(_Float16 b) { *this *= VectorF16<Len, Packing>(b); }
constexpr void operator/=(_Float16 b) { *this /= VectorF16<Len, Packing>(b); }
constexpr VectorF16<Len, Packing> operator-() const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = -this->v[i];
return r;
}
constexpr bool operator==(VectorF16<Len, Packing> b) const {
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
if (this->v[p * Len + i] != b.v[p * Len + i]) return false;
return true;
}
constexpr bool operator!=(VectorF16<Len, Packing> b) const { return !(*this == b); }
template<std::uint32_t ExtractLen>
constexpr VectorF16<ExtractLen, Packing> ExtractLo() const {
VectorF16<ExtractLen, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < ExtractLen; ++i)
r.v[p * ExtractLen + i] = this->v[p * Len + i];
return r;
}
// Transcendentals are computed via float since libstdc++ doesn't always
// provide overloads for _Float16; the result is rounded back to half.
constexpr VectorF16<Len, Packing> Cos() const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = static_cast<_Float16>(std::cos(static_cast<float>(this->v[i])));
return r;
}
constexpr VectorF16<Len, Packing> Sin() const {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = static_cast<_Float16>(std::sin(static_cast<float>(this->v[i])));
return r;
}
constexpr std::tuple<VectorF16<Len, Packing>, VectorF16<Len, Packing>> SinCos() const {
return { Sin(), Cos() };
}
template <std::array<bool, Len> values>
constexpr VectorF16<Len, Packing> Negate() const {
VectorF16<Len, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
r.v[p * Len + i] = values[i] ? -this->v[p * Len + i] : this->v[p * Len + i];
return r;
}
static constexpr VectorF16<Len, Packing> MulitplyAdd(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> add) {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = a.v[i] * b.v[i] + add.v[i];
return r;
}
static constexpr VectorF16<Len, Packing> MulitplySub(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b, VectorF16<Len, Packing> sub) {
VectorF16<Len, Packing> r;
for (std::uint8_t i = 0; i < NElems; ++i) r.v[i] = a.v[i] * b.v[i] - sub.v[i];
return r;
}
constexpr static VectorF16<Len, Packing> Cross(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) requires(Len == 3) {
VectorF16<Len, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p) {
const std::uint8_t base = p * 3;
r.v[base + 0] = a.v[base + 1] * b.v[base + 2] - a.v[base + 2] * b.v[base + 1];
r.v[base + 1] = a.v[base + 2] * b.v[base + 0] - a.v[base + 0] * b.v[base + 2];
r.v[base + 2] = a.v[base + 0] * b.v[base + 1] - a.v[base + 1] * b.v[base + 0];
}
return r;
}
template <const std::array<std::uint8_t, Len> ShuffleValues>
constexpr VectorF16<Len, Packing> Shuffle() const {
VectorF16<Len, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
r.v[p * Len + i] = this->v[p * Len + ShuffleValues[i]];
return r;
}
template <std::array<bool, Len> ShuffleValues>
constexpr static VectorF16<Len, Packing> Blend(VectorF16<Len, Packing> a, VectorF16<Len, Packing> b) {
VectorF16<Len, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p)
for (std::uint8_t i = 0; i < Len; ++i)
r.v[p * Len + i] = ShuffleValues[i] ? b.v[p * Len + i] : a.v[p * Len + i];
return r;
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto LengthSq(VectorF16<Len, Packing> first, Rest... rest) {
constexpr std::uint8_t N = VectorBase<Len, Packing, _Float16>::BatchSize;
VectorF16<1, static_cast<std::uint8_t>(Packing * N)> r;
std::array<VectorF16<Len, Packing>, N> args{ first, rest... };
for (std::uint8_t i = 0; i < N; ++i)
for (std::uint8_t p = 0; p < Packing; ++p) {
_Float16 acc = _Float16(0);
for (std::uint8_t k = 0; k < Len; ++k) {
_Float16 x = args[i].v[p * Len + k];
acc += x * x;
}
r.v[i * Packing + p] = acc;
}
return r;
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Length(VectorF16<Len, Packing> first, Rest... rest) {
auto sq = LengthSq(first, rest...);
for (std::uint8_t i = 0; i < decltype(sq)::NElems; ++i)
sq.v[i] = static_cast<_Float16>(std::sqrt(static_cast<float>(sq.v[i])));
return sq;
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF16<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, _Float16>::BatchSize))
constexpr static auto Normalize(VectorF16<Len, Packing> first, Rest... rest) {
auto normOne = [](VectorF16<Len, Packing> u) {
VectorF16<Len, Packing> out;
for (std::uint8_t p = 0; p < Packing; ++p) {
float acc = 0.0f;
for (std::uint8_t k = 0; k < Len; ++k) {
float x = static_cast<float>(u.v[p * Len + k]);
acc += x * x;
}
_Float16 invLen = acc > 0.0f ? static_cast<_Float16>(1.0f / std::sqrt(acc)) : _Float16(0);
for (std::uint8_t k = 0; k < Len; ++k)
out.v[p * Len + k] = u.v[p * Len + k] * invLen;
}
return out;
};
return std::array<VectorF16<Len, Packing>, VectorBase<Len, Packing, _Float16>::BatchSize>{ normOne(first), normOne(rest)... };
}
constexpr static VectorF16<Len, Packing> Rotate(VectorF16<3, Packing> v, VectorF16<4, Packing> q) requires(Len == 3) {
VectorF16<3, Packing> qv;
VectorF16<3, Packing> qwBroadcast;
for (std::uint8_t p = 0; p < Packing; ++p) {
qv.v[p * 3 + 0] = q.v[p * 4 + 0];
qv.v[p * 3 + 1] = q.v[p * 4 + 1];
qv.v[p * 3 + 2] = q.v[p * 4 + 2];
for (std::uint8_t i = 0; i < 3; ++i) qwBroadcast.v[p * 3 + i] = q.v[p * 4 + 3];
}
VectorF16<3, Packing> t = Cross(qv, v) * _Float16(2);
return v + t * qwBroadcast + Cross(qv, t);
}
constexpr static VectorF16<3, Packing> RotatePivot(VectorF16<3, Packing> v, VectorF16<4, Packing> q, VectorF16<3, Packing> pivot) requires(Len == 3) {
VectorF16<3, Packing> translated = v - pivot;
return Rotate(translated, q) + pivot;
}
constexpr static VectorF16<4, Packing> QuanternionFromEuler(VectorF16<3, Packing> eulerHalf) requires(Len == 4) {
VectorF16<4, Packing> r;
for (std::uint8_t p = 0; p < Packing; ++p) {
float roll = static_cast<float>(eulerHalf.v[p * 3 + 0]);
float pitch = static_cast<float>(eulerHalf.v[p * 3 + 1]);
float yaw = static_cast<float>(eulerHalf.v[p * 3 + 2]);
float sr = std::sin(roll), cr = std::cos(roll);
float sp = std::sin(pitch), cp = std::cos(pitch);
float sy = std::sin(yaw), cy = std::cos(yaw);
r.v[p * 4 + 0] = static_cast<_Float16>(sr * cp * cy - cr * sp * sy);
r.v[p * 4 + 1] = static_cast<_Float16>(cr * sp * cy + sr * cp * sy);
r.v[p * 4 + 2] = static_cast<_Float16>(cr * cp * sy - sr * sp * cy);
r.v[p * 4 + 3] = static_cast<_Float16>(cr * cp * cy + sr * sp * sy);
}
return r;
}
};
}
#endif
#if defined(__FLT16_MAX__) && (!defined(__x86_64) || defined(__AVX512FP16__)) && !(defined(__aarch64__) && defined(__ARM_NEON) && !defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC))
export template <std::uint32_t Len, std::uint32_t Packing>
struct std::formatter<Crafter::VectorF16<Len, Packing>> : std::formatter<std::string> {
constexpr auto format(const Crafter::VectorF16<Len, Packing>& obj, format_context& ctx) const {
std::array<_Float16, Crafter::VectorF16<Len, Packing>::AlignmentElement> vec = obj.template Store<float>();
std::string out = "{";
for(std::uint32_t i = 0; i < Packing; i++) {
out += "{";
for(std::uint32_t i2 = 0; i2 < Len; i2++) {
out += std::format("{}", static_cast<float>(vec[i * Len + i2]));
if (i2 + 1 < Len) out += ",";
}
out += "}";
}
out += "}";
return std::formatter<std::string>::format(out, ctx);
}
};
2026-03-19 02:19:01 +01:00
#endif