4#include "native/isa_import.h"
5#include <native/simd.h>
6#include <native/integer.h>
7#include <native/packing.h>
8#include <native/simd/math/bits.h>
9#include <native/targets.h>
17extern "C++" namespace native {
19 template<
class T, std::
size_t N, isa<> Arch = NATIVE_BA
SELINE>
struct simd;
23#include "native/simd/exports.h"
25#define NATIVE_ARCH_REQUIRES(A) (::native::avx512_bf16 <= A)
26#pragma clang attribute push(__attribute__((target("avx2,fma,avx512f,avx512dq,avx512bw,avx512vl,avx512bf16"))), apply_to=function)
29 template<std::
size_t N,::native::isa<> A>
requires NATIVE_ARCH_REQUIRES(A) &&(N==8 || N==16 || N==32)
30 struct value_traits<simd<bf16,N,A>> {
31 static constexpr isa<> value=avx512_bf16;
32 static constexpr bool known=
true;
33 static constexpr bool aggregate_default=
false;
43 template<std::
size_t N, ::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch) &&(N == 8 || N == 16 || N == 32)
52 using native_type = std::conditional_t<N == 8,__m128bh,std::conditional_t<N == 16,__m256bh,__m512bh>>;
67 static constexpr std::size_t
lanes = N;
79 template<
class... T>
requires(
sizeof...(T) == lanes && (std::same_as<T,bf16> && ...))
86#include "native/simd/bf16_reject_operators.h"
91 simd result; result.value_ = value;
return result;
95 return bits_type::from_native(__builtin_bit_cast(
typename bits_type::native_type,value_));
115 template<std::
size_t Alignment = 1>
117 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
119 std::array<std::uint16_t,lanes> words{};
120 for (std::size_t i=0;i<
lanes;++i) words[i]=p[i].
to_bits();
129 template<std::
size_t Alignment = 1>
131 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
133 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
137 std::memcpy(p, &value_,
sizeof(value_));
154 std::array<bf16,lanes> values; values.fill(fill);
156 for (std::size_t i=0;i<n;++i) values[i]=p[i];
157 }
else {
if (n) std::memcpy(values.data(), p, n *
sizeof(
bf16)); }
158 return load(values.data());
166 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
168 }
else {
if (n) std::memcpy(p, &value_, n *
sizeof(
bf16)); }
179 template<std::
size_t N, ::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch) &&(N == 8 || N == 16 || N == 32)
183 if consteval {
return detail::half_constant::dot2<false>(a,b,accumulator); }
184 return simd<float,N/2,Arch>::from_native(
185 detail::avx512_bf16_backend::dot2_native(a.to_native(), b.to_native(), accumulator.to_native()));
189#pragma clang attribute pop
190#undef NATIVE_ARCH_REQUIRES
191#define NATIVE_ARCH_REQUIRES(A) (::native::avx512_fp16 <= A)
192#pragma clang attribute push(__attribute__((target("avx2,fma,avx512f,avx512dq,avx512bw,avx512vl,avx512fp16"))), apply_to=function)
195 template<::native::isa<> A>
requires NATIVE_ARCH_REQUIRES(A)
196 struct value_traits<simd<fp16,32,A>> {
197 static constexpr isa<> value=avx512_fp16;
198 static constexpr bool known=
true;
199 static constexpr bool aggregate_default=
false;
214 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
struct simd<
fp16,32,Arch> {
237 static constexpr std::size_t
lanes = 32;
249 template<
class... T>
requires(
sizeof...(T) == lanes && (std::same_as<T,fp16> && ...))
259 simd result; result.value_ = value;
return result;
263 return bits_type::from_native(__builtin_bit_cast(
typename bits_type::native_type,value_));
283 template<std::
size_t Alignment = 1>
285 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
287 std::array<std::uint16_t,lanes> words{};
288 for (std::size_t i=0;i<
lanes;++i) words[i]=p[i].
to_bits();
297 template<std::
size_t Alignment = 1>
299 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
301 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
305 std::memcpy(p, &value_,
sizeof(value_));
322 std::array<fp16,lanes> values; values.fill(fill);
324 for (std::size_t i=0;i<n;++i) values[i]=p[i];
325 }
else {
if (n) std::memcpy(values.data(), p, n *
sizeof(
fp16)); }
326 return load(values.data());
334 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
336 }
else {
if (n) std::memcpy(p, &value_, n *
sizeof(
fp16)); }
340 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::add,false>(a,b); }
341 return from_native(detail::avx512_fp16_backend::add_half(a.value_,b.value_));
345 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::subtract,false>(a,b); }
346 return from_native(detail::avx512_fp16_backend::sub_half(a.value_,b.value_));
350 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::multiply,false>(a,b); }
351 return from_native(detail::avx512_fp16_backend::mul_half(a.value_,b.value_));
356 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::divide,false>(a,b); }
357 return from_native(detail::avx512_fp16_backend::div_half(a.value_,b.value_));
363 if consteval {
return detail::half_constant::square_root<false>(a); }
364 return from_native(detail::avx512_fp16_backend::sqrt_half(a.value_));
368 if consteval {
return detail::half_constant::unary(a,[](
auto x) {
return std::uint16_t(x^0x8000); }); }
369 return from_native(detail::avx512_fp16_backend::neg_half(a.value_));
374 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
375 return mask::from_native(detail::avx512_fp16_backend::eq_half(a.value_,b.value_));
381 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y); }); }
382 return mask::from_native(detail::avx512_fp16_backend::lt_half(a.value_,b.value_));
386 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y) || detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
387 return mask::from_native(detail::avx512_fp16_backend::le_half(a.value_,b.value_));
396 if consteval {
return detail::half_constant::select(m,a,b); }
397 return from_native(detail::avx512_fp16_backend::select_half(m.to_native(),a.value_,b.value_));
405 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
408 if consteval {
return detail::half_constant::fused<false>(a,b,c); }
410 detail::avx512_fp16_backend::fma_half(a.to_native(),b.to_native(),c.to_native()));
414#pragma clang attribute pop
415#undef NATIVE_ARCH_REQUIRES
417#if NATIVE_HOST_NEON || defined(NATIVE_DOXYGEN)
418#define NATIVE_ARCH_REQUIRES(A) (::native::neon_bf16 <= A)
419#pragma clang attribute push(__attribute__((target("neon,bf16"))), apply_to=function)
422 template<::native::isa<> A>
requires NATIVE_ARCH_REQUIRES(A)
423 struct value_traits<simd<bf16,8,A>> {
424 static constexpr isa<> value=neon_bf16;
425 static constexpr bool known=
true;
426 static constexpr bool aggregate_default=
false;
436 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
struct simd<
bf16,8,Arch> {
459 static constexpr std::size_t
lanes = 8;
471 template<
class... T>
requires(
sizeof...(T) == lanes && (std::same_as<T,bf16> && ...))
478#include "native/simd/bf16_reject_operators.h"
483 simd result; result.value_ = value;
return result;
487 return bits_type::from_native(__builtin_bit_cast(
typename bits_type::native_type,value_));
507 template<std::
size_t Alignment = 1>
509 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
511 std::array<std::uint16_t,lanes> words{};
512 for (std::size_t i=0;i<
lanes;++i) words[i]=p[i].
to_bits();
521 template<std::
size_t Alignment = 1>
523 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
525 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
529 std::memcpy(p, &value_,
sizeof(value_));
546 std::array<bf16,lanes> values; values.fill(fill);
548 for (std::size_t i=0;i<n;++i) values[i]=p[i];
549 }
else {
if (n) std::memcpy(values.data(), p, n *
sizeof(
bf16)); }
550 return load(values.data());
558 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
560 }
else {
if (n) std::memcpy(p, &value_, n *
sizeof(
bf16)); }
576 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
579 simd<
float,4,Arch> accumulator) noexcept {
580 if consteval {
return detail::half_constant::dot2<true>(a,b,accumulator); }
582 detail::neon_bf16_backend::dot2_native(a.to_native(), b.to_native(), accumulator.to_native()));
586#pragma clang attribute pop
587#undef NATIVE_ARCH_REQUIRES
588#define NATIVE_ARCH_REQUIRES(A) (::native::neon_fp16 <= A)
589#pragma clang attribute push(__attribute__((target("neon,fullfp16"))), apply_to=function)
592 template<::native::isa<> A>
requires NATIVE_ARCH_REQUIRES(A)
593 struct value_traits<simd<fp16,8,A>> {
594 static constexpr isa<> value=neon_fp16;
595 static constexpr bool known=
true;
596 static constexpr bool aggregate_default=
false;
611 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
struct simd<
fp16,8,Arch> {
634 static constexpr std::size_t
lanes = 8;
646 template<
class... T>
requires(
sizeof...(T) == lanes && (std::same_as<T,fp16> && ...))
656 simd result; result.value_ = value;
return result;
660 return bits_type::from_native(__builtin_bit_cast(
typename bits_type::native_type,value_));
680 template<std::
size_t Alignment = 1>
682 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
684 std::array<std::uint16_t,lanes> words{};
685 for (std::size_t i=0;i<
lanes;++i) words[i]=p[i].
to_bits();
694 template<std::
size_t Alignment = 1>
696 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
698 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
702 std::memcpy(p, &value_,
sizeof(value_));
719 std::array<fp16,lanes> values; values.fill(fill);
721 for (std::size_t i=0;i<n;++i) values[i]=p[i];
722 }
else {
if (n) std::memcpy(values.data(), p, n *
sizeof(
fp16)); }
723 return load(values.data());
731 std::array<std::uint16_t,lanes> words{};
store_bits(words.data());
733 }
else {
if (n) std::memcpy(p, &value_, n *
sizeof(
fp16)); }
737 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::add,true>(a,b); }
738 return from_native(detail::neon_fp16_backend::add_half(a.value_,b.value_));
742 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::subtract,true>(a,b); }
743 return from_native(detail::neon_fp16_backend::sub_half(a.value_,b.value_));
747 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::multiply,true>(a,b); }
748 return from_native(detail::neon_fp16_backend::mul_half(a.value_,b.value_));
753 if consteval {
return detail::half_constant::arithmetic<detail::half_constant::operation::divide,true>(a,b); }
754 return from_native(detail::neon_fp16_backend::div_half(a.value_,b.value_));
761 if consteval {
return detail::half_constant::square_root<true>(a); }
762 return from_native(detail::neon_fp16_backend::sqrt_half(a.value_));
766 if consteval {
return detail::half_constant::unary(a,[](
auto x) {
return std::uint16_t(x^0x8000); }); }
767 return from_native(detail::neon_fp16_backend::neg_half(a.value_));
772 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
773 return mask::unsafe_from_native(detail::neon_fp16_backend::eq_half(a.value_,b.value_));
779 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y); }); }
780 return mask::unsafe_from_native(detail::neon_fp16_backend::lt_half(a.value_,b.value_));
784 if consteval {
return detail::half_constant::compare(a,b,[](
auto x,
auto y) {
return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y) || detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
785 return mask::unsafe_from_native(detail::neon_fp16_backend::le_half(a.value_,b.value_));
794 if consteval {
return detail::half_constant::select(m,a,b); }
795 return from_native(detail::neon_fp16_backend::select_half(m.to_native(),a.value_,b.value_));
803 template<::native::isa<> Arch>
requires NATIVE_ARCH_REQUIRES(Arch)
806 if consteval {
return detail::half_constant::fused<true>(a,b,c); }
808 detail::neon_fp16_backend::fma_half(a.to_native(),b.to_native(),c.to_native()));
812#pragma clang attribute pop
813#undef NATIVE_ARCH_REQUIRES
821 template<
class T>
concept instruction_element = simd_integer_element<T> ||
822 std::same_as<T,float> || std::same_as<T,double> ||
823 std::same_as<T,fp16> || std::same_as<T,bf16>;
825 template<
class T, std::
size_t N, isa<> A>
826 inline constexpr bool instruction_storage_shape = [] {
827 if constexpr(!instruction_element<T> || N<2)
return false;
828 else if constexpr(N>64/
sizeof(T))
return false;
830 constexpr auto bytes=
sizeof(T)*N;
832 if constexpr(!(neon<=A))
return false;
833 if constexpr(std::same_as<T,fp16>)
return N==4 || (N==8 && !(neon_fp16<=A));
834 if constexpr(std::same_as<T,bf16>)
return N==4 || (N==8 && !(neon_bf16<=A));
835 if constexpr(std::same_as<T,double>)
return N==2;
837 return simd_integer_element<T> &&
sizeof(T)<4 && bytes==8;
839 if constexpr(!A.has(x86_feature::sse2))
return false;
840 if constexpr(std::same_as<T,fp16>)
return N==4 || N==8;
841 if constexpr(std::same_as<T,bf16>)
return N==4 || (N==8 && !(avx512_bf16<=A));
842 if constexpr(bytes==8)
return simd_integer_element<T> &&
sizeof(T)<4;
843 if constexpr(bytes==16 || bytes==32) {
844 if constexpr(bytes==32 && !A.has(x86_feature::avx))
return false;
845 return std::same_as<T,double> || !(avx2<=A);
847 if constexpr(bytes==64 && A.has(x86_feature::avx512f)) {
848 if constexpr(std::same_as<T,double>)
return true;
849 return !(kernel_base<=A) || (
sizeof(T)<4 && !A.has(x86_feature::avx512bw));
858 template<std::
size_t N,isa<> A>
inline constexpr bool instruction_predicate_shape =
861 A.has(x86_feature::sse2) && !((kernel_base<=A) &&
862 (N==1 || N==2 || N==3 || N==4 || N==8 || N==16 ||
863 ((N==32 || N==64) && A.has(x86_feature::avx512bw))));
864#elif NATIVE_HOST_NEON
870 template<
class T,std::
size_t N,isa<> A>
871 requires ordinary_simd_element<T> && instruction_storage_shape<T,N,A>
872 struct value_traits<simd<T,N,A>> {
873 static constexpr isa<> value=A;
874 static constexpr bool known=
true;
875 static constexpr bool aggregate_default=
false;
877 template<std::
size_t N,isa<> A>
requires instruction_predicate_shape<N,A>
878 struct value_traits<predicate<N,A>> {
879 static constexpr isa<> value=A;
880 static constexpr bool known=
true;
881 static constexpr bool aggregate_default=
false;
884 template<
class T, std::
size_t N>
struct instruction_register {
886 using type=std::conditional_t<std::same_as<T,double>,float64x2_t,
887 std::conditional_t<(
sizeof(T)*N<=8),uint8x8_t,uint8x16_t>>;
889 using type=std::conditional_t<std::same_as<T,float>,
890 std::conditional_t<(
sizeof(T)*N<=16),__m128,std::conditional_t<(
sizeof(T)*N==32),__m256,__m512>>,
891 std::conditional_t<std::same_as<T,double>,
892 std::conditional_t<(
sizeof(T)*N<=16),__m128d,std::conditional_t<(
sizeof(T)*N==32),__m256d,__m512d>>,
893 std::conditional_t<(
sizeof(T)*N<=16),__m128i,std::conditional_t<(
sizeof(T)*N==32),__m256i,__m512i>>>>;
899 template<std::
size_t N, isa<> A>
requires detail::instruction_predicate_shape<N,A>
901 static constexpr isa<> architecture=A;
902 static constexpr std::size_t lanes=N;
903 using native_type=std::conditional_t<(N<=8),std::uint8_t,
904 std::conditional_t<(N<=16),std::uint16_t,std::conditional_t<(N<=32),std::uint32_t,std::uint64_t>>>;
907 static constexpr bool compact=
true;
909 native_type value_{};
910 static constexpr std::uint64_t active=[] {
if constexpr(N==64)
return ~std::uint64_t{};
else return (std::uint64_t{1}<<N)-1; }();
961 template<
class T, std::
size_t N, isa<> A>
requires detail::instruction_storage_shape<T,N,A>
962 struct alignas(typename detail::instruction_register<T,N>::type)
simd<T,N,A> {
964 using register_type=
simd;
965 using native_type=
typename detail::instruction_register<T,N>::type;
966 static constexpr isa<> architecture=A;
967 static constexpr std::size_t lanes=N;
969 using mask_type=mask;
970 using predicate_type=mask;
971 using bits_type=
simd<std::conditional_t<
sizeof(T)==1,std::uint8_t,
972 std::conditional_t<
sizeof(T)==2,std::uint16_t,
973 std::conditional_t<
sizeof(T)==4,std::uint32_t,std::uint64_t>>>,N,A>;
980#define NATIVE_INSTRUCTION_HALF_REJECT_BINARY(OP) \
982 friend void operator OP(simd,simd) \
983 requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete; \
985 template<class U> requires((std::same_as<T,fp16> || std::same_as<T,bf16>) && \
986 !std::same_as<std::remove_cvref_t<U>,simd>) \
987 friend void operator OP(simd,U) = delete; \
989 template<class U> requires((std::same_as<T,fp16> || std::same_as<T,bf16>) && \
990 !std::same_as<std::remove_cvref_t<U>,simd>) \
991 friend void operator OP(U,simd) = delete;
992 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(+)
993 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(-)
994 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(*)
995 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(/)
996 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(%)
997 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(&)
998 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(|)
999 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(^)
1000 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<<)
1001 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>>)
1002 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(==)
1003 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(!=)
1004 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<)
1005 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<=)
1006 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>)
1007 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>=)
1008#undef NATIVE_INSTRUCTION_HALF_REJECT_BINARY
1009#define NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(OP) \
1011 template<class U> requires(std::same_as<T,fp16> || std::same_as<T,bf16>) \
1012 void operator OP(U) = delete;
1013 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(+=)
1014 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(-=)
1015 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(*=)
1016 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(/=)
1017 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(%=)
1018 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(&=)
1019 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(|=)
1020 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(^=)
1021 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(<<=)
1022 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(>>=)
1023#undef NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN
1025 friend void operator+(
simd)
requires(std::same_as<T,fp16> || std::same_as<T,bf16>) =
delete;
1027 friend void operator-(
simd)
requires(std::same_as<T,fp16> || std::same_as<T,bf16>) =
delete;
1029 friend void operator~(
simd)
requires(std::same_as<T,fp16> || std::same_as<T,bf16>) =
delete;
1031 friend void operator!(
simd)
requires(std::same_as<T,fp16> || std::same_as<T,bf16>) =
delete;
1036 std::array<T,N> values;
1038 *
this=
load(values.data());
1043 template<
class... U>
requires(
sizeof...(U)==N && (std::same_as<U,T> && ...))
1050 native_type
to_native() const noexcept requires(sizeof(native_type)==16) {
return value_; }
1054 simd result; result.value_=value;
return result;
1059 simd result; result.value_=value;
return result;
1063 native_type
to_native() const noexcept requires(sizeof(native_type)==32) {
return value_; }
1067 simd result; result.value_=value;
return result;
1072 simd result; result.value_=value;
return result;
1076 native_type
to_native() const noexcept requires(sizeof(native_type)==64) {
return value_; }
1080 simd result; result.value_=value;
return result;
1085 simd result; result.value_=value;
return result;
1092 simd result; result.value_=value;
return result;
1096 template<std::
size_t Alignment=1>
1098 static_assert(Alignment>0 && (Alignment&(Alignment-1))==0);
1101 std::array<T,
sizeof(native_type)/
sizeof(T)> values{};
1102 for(std::size_t i=0;i<N;++i) values[i]=p[i];
1103 result.value_=std::bit_cast<native_type>(values);
1104 }
else { std::memcpy(&result.value_,p,
sizeof(T)*N); }
1108 template<std::
size_t Alignment=1>
1110 static_assert(Alignment>0 && (Alignment&(Alignment-1))==0);
1112 auto values=std::bit_cast<std::array<T,
sizeof(native_type)/
sizeof(T)>>(value_);
1113 for(std::size_t i=0;i<N;++i) p[i]=values[i];
1114 }
else { std::memcpy(p,&value_,
sizeof(T)*N); }
1127 std::array<T,N> values; values.fill(fill);
1129 for(std::size_t i=0;i<n;++i) values[i]=p[i];
1130 }
else {
if(n) std::memcpy(values.data(),p,n*
sizeof(T)); }
1131 return load(values.data());
1138 auto values=std::bit_cast<std::array<T,
sizeof(native_type)/
sizeof(T)>>(value_);
1139 for(std::size_t i=0;i<n;++i) p[i]=values[i];
1141 }
else {
if(n) std::memcpy(p,&value_,n*
sizeof(T)); }
1145 return bits_type::from_native(std::bit_cast<typename bits_type::native_type>(value_));
1151 return from_native(std::bit_cast<native_type>(words.to_native()));
1155 if consteval {
return from_bits(bits_type::load(p)); }
1156 else {
simd result{}; std::memcpy(&result.value_,p,2*N);
return result; }
1159 native_inline constexpr void store_bits(std::uint16_t * p)
const noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) {
1160 if consteval {
bits().store(p); }
1161 else { std::memcpy(p,&value_,2*N); }
#define native_diagnose_if(condition, message)
Reject a call when Clang can prove that its arguments violate a precondition.
#define native_inline
inline [[always_inline]]
#define native_nodiscard
C++17 [[nodiscard]].
#define native_target(x)
this indicates a required feature set for the current multiversioned function.
Scalar half storage, numeric limits and legacy scalar predicates.
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr simd< float, N/2, Arch > dot2(simd< bf16, N, Arch > a, simd< bf16, N, Arch > b, simd< float, N/2, Arch > accumulator) noexcept
constexpr simd< fp16, 32, Arch > fma(simd< fp16, 32, Arch > a, simd< fp16, 32, Arch > b, simd< fp16, 32, Arch > c) noexcept
Standard-library adaptations documented here for SIMD value types.
static constexpr bf16 from_bits(uint16_t data) noexcept
Adopt the exact sixteen representation bits without normalization.
static constexpr fp16 from_bits(uint16_t data) noexcept
Adopt the exact sixteen representation bits without normalization.
constexpr std::uint64_t to_bitset() const noexcept
Return the logical lane bitset.
static constexpr predicate from_bits(std::uint64_t bits) noexcept
Read one bit per lane and clear bits above the logical lane count.
friend constexpr bool all(predicate p) noexcept
Test whether every logical lane is set.
static constexpr predicate from_bitset(std::uint64_t bits) noexcept
Construct from the logical lane bitset.
friend constexpr predicate operator~(predicate p) noexcept
Complement logical lanes, leaving padding clear.
constexpr std::uint64_t bits() const noexcept
Return one bit per logical lane.
friend constexpr predicate operator==(predicate a, predicate b) noexcept
Mark lanes whose truth values agree.
friend constexpr bool none(predicate p) noexcept
Test whether every logical lane is clear.
constexpr native_type to_native() const noexcept
Return the compact implementation representation.
constexpr predicate & operator^=(predicate b) noexcept
Toggle lanes present in another mask.
friend constexpr predicate operator!(predicate p) noexcept
Complement each logical lane.
static constexpr predicate from_native(native_type bits) noexcept
Adopt the compact representation, clearing unused bits.
friend constexpr predicate operator&(predicate a, predicate b) noexcept
Intersect the logical lane masks.
friend constexpr bool any(predicate p) noexcept
Test whether any logical lane is set.
friend constexpr predicate operator^(predicate a, predicate b) noexcept
Toggle lanes present in exactly one operand.
friend constexpr predicate select(predicate p, predicate a, predicate b) noexcept
Choose each mask lane from a or b according to p.
friend constexpr predicate operator!=(predicate a, predicate b) noexcept
Mark lanes whose truth values differ.
constexpr predicate & operator&=(predicate b) noexcept
Intersect with another mask in place.
friend constexpr predicate operator|(predicate a, predicate b) noexcept
Unite the logical lane masks.
constexpr predicate() noexcept=default
Construct an empty mask.
constexpr predicate & operator|=(predicate b) noexcept
Unite with another mask in place.
static constexpr simd load(T const *p) noexcept
Read exactly N elements with their natural alignment.
constexpr void store_memory(T *p) const noexcept
Write exactly N objects; Alignment is a caller promise.
friend void operator!(simd)=delete
Half storage does not define a predicate conversion.
friend void operator-(simd)=delete
Reject unary arithmetic on half instruction storage.
constexpr simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
constexpr void store_bits(std::uint16_t *p) const noexcept
Write N unsigned half representations without numerical conversion.
constexpr void store_partial(T *p, std::size_t n) const noexcept
Write the first n <= N elements; a null pointer is valid for n == 0.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
Read N unsigned half representations without numerical conversion.
constexpr native_type to_native() const noexcept
Bridge to the implementation register without numerical conversion.
static constexpr simd unsafe_from_native(native_type value) noexcept
Synonym for from_native; these storage-only shapes do not normalize padding.
static constexpr simd loadu(T const *p) noexcept
Synonym for load; register alignment is unnecessary.
constexpr simd(std::array< T, N > const &values) noexcept
Copy the array elements in lane order.
constexpr bits_type bits() const noexcept
Return each half lane as its unchanged unsigned representation.
friend void operator~(simd)=delete
Use bits() for explicit bitwise operations on half representations.
static constexpr simd from_native(native_type value) noexcept
Adopt register bits unchanged; unused physical bytes are unspecified.
friend void operator+(simd)=delete
Reject unary arithmetic on half instruction storage.
static constexpr simd load_memory(T const *p) noexcept
Read exactly N objects, without requiring register-width alignment.
constexpr bits_type to_bits() const noexcept
Synonym for bits; no floating-point conversion occurs.
constexpr void store(T *p) const noexcept
Write exactly N elements in lane order.
static constexpr simd load_partial(T const *p, std::size_t n, T fill=T{}) noexcept
Read n <= N elements and fill the remainder; a null pointer is valid for n == 0.
static constexpr simd from_bits(bits_type words) noexcept
Interpret unsigned words as half representations without conversion.
constexpr void storeu(T *p) const noexcept
Synonym for store; register alignment is unnecessary.
constexpr void store_partial(bf16 *p, std::size_t n) const noexcept
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
simd< std::uint16_t, 8, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a BF16 representation without changing its bits.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
static constexpr std::size_t lanes
Number of logical BF16 lanes, with no padding lanes.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr void store(bf16 *p) const noexcept
Store 8 BF16 objects with the default alignment contract of store_memory().
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
constexpr void store_memory(bf16 *p) const noexcept
static constexpr simd loadu(bf16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
simd< T, 8, architecture > rebind
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
static constexpr simd load_memory(bf16 const *p) noexcept
simd< mask16, 8, architecture > mask
Full-register predicate with zero or all-one 16-bit lanes.
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
constexpr void store_bits(std::uint16_t *p) const noexcept
static constexpr isa architecture
The distinct compile-time NEON_BF16 instruction profile.
mask predicate_type
Explicit predicate spelling for the same full-register mask type.
bf16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr simd load(bf16 const *p) noexcept
Load 8 BF16 objects with the default alignment contract of load_memory().
constexpr void storeu(bf16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
static constexpr simd from_native(native_type value) noexcept
Copy a native BF16 register into this vector, preserving every representation bit.
bfloat16x8_t native_type
Native 128-bit BF16 register representation; native bridges copy bits.
constexpr simd(std::array< bf16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
simd register_type
This one-register vector type, for generic register-based algorithms.
mask mask_type
Generic mask spelling for the full-register lane mask.
simd< mask16, 8, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
static constexpr simd load_partial(bf16 const *p, std::size_t n, bf16 fill=bf16::from_bits(0)) noexcept
static constexpr std::size_t lanes
Number of logical BF16 lanes, with no padding lanes.
static constexpr simd load_memory(bf16 const *p) noexcept
static constexpr simd loadu(bf16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
constexpr void store_memory(bf16 *p) const noexcept
static constexpr simd load_bits(std::uint16_t const *p) noexcept
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
constexpr void store_bits(std::uint16_t *p) const noexcept
std::conditional_t< N==8, __m128bh, std::conditional_t< N==16, __m256bh, __m512bh > > native_type
Native register-width BF16 register representation; native bridges copy bits.
constexpr simd(std::array< bf16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
constexpr void store_partial(bf16 *p, std::size_t n) const noexcept
simd< mask16, N, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
bf16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr isa architecture
The distinct compile-time AVX512_BF16 instruction profile.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr void storeu(bf16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
static constexpr simd load(bf16 const *p) noexcept
Load N BF16 objects with the default alignment contract of load_memory().
static constexpr simd load_partial(bf16 const *p, std::size_t n, bf16 fill=bf16::from_bits(0)) noexcept
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store(bf16 *p) const noexcept
Store N BF16 objects with the default alignment contract of store_memory().
simd< std::uint16_t, N, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
mask mask_type
Generic mask spelling for the compact lane predicate.
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a BF16 representation without changing its bits.
predicate< N, architecture > mask
Compact predicate with bit i selecting BF16 lane i.
mask predicate_type
Explicit predicate spelling for the same compact mask type.
simd< T, N, architecture > rebind
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
simd register_type
This one-register vector type, for generic register-based algorithms.
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
static constexpr simd from_native(native_type value) noexcept
Copy a native BF16 register into this vector, preserving every representation bit.
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store_partial(fp16 *p, std::size_t n) const noexcept
constexpr void store_bits(std::uint16_t *p) const noexcept
friend constexpr simd sqrt(simd a) noexcept
simd< T, 32, architecture > rebind
friend constexpr mask operator>=(simd a, simd b) noexcept
Ordered lane greater-or-equal, with native less-or-equal operands reversed.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
simd register_type
This one-register vector type, for generic register-based algorithms.
constexpr void store(fp16 *p) const noexcept
Store 32 FP16 objects with the default alignment contract of store_memory().
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
friend constexpr simd operator/(simd a, simd b) noexcept
simd< std::uint16_t, 32, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
static constexpr simd from_native(native_type value) noexcept
Copy a native FP16 register into this vector, preserving every representation bit.
friend constexpr mask operator==(simd a, simd b) noexcept
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
friend constexpr simd select(mask m, simd a, simd b) noexcept
static constexpr simd load_memory(fp16 const *p) noexcept
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a FP16 representation without changing its bits.
predicate< 32, architecture > mask
Compact predicate with bit i selecting FP16 lane i.
fp16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr isa architecture
The distinct compile-time AVX512_FP16 instruction profile.
friend constexpr mask operator!=(simd a, simd b) noexcept
Lane inequality, true for unordered NaN operands; complements native equality.
static constexpr simd load_partial(fp16 const *p, std::size_t n, fp16 fill=fp16::from_bits(0)) noexcept
static constexpr simd load(fp16 const *p) noexcept
Load 32 FP16 objects with the default alignment contract of load_memory().
friend constexpr mask operator<=(simd a, simd b) noexcept
Ordered lane less-or-equal. NaNs compare false; native MXCSR exception semantics apply.
constexpr void store_memory(fp16 *p) const noexcept
static constexpr simd loadu(fp16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
friend constexpr simd operator-(simd a) noexcept
Toggle every sign bit, preserving all payload bits without arithmetic exceptions.
constexpr simd(std::array< fp16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
constexpr void storeu(fp16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
friend constexpr simd operator*(simd a, simd b) noexcept
Multiply corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
simd< mask16, 32, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
mask mask_type
Generic mask spelling for the compact lane predicate.
friend constexpr simd operator+(simd a, simd b) noexcept
Add corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
mask predicate_type
Explicit predicate spelling for the same compact predicate type.
friend constexpr simd operator-(simd a, simd b) noexcept
Subtract corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
friend constexpr mask operator<(simd a, simd b) noexcept
Ordered lane less-than. NaNs compare false; native MXCSR exception semantics apply.
__m512h native_type
Native 512-bit FP16 register representation; native bridges copy bits.
static constexpr std::size_t lanes
Number of logical FP16 lanes, with no padding lanes.
friend constexpr mask operator>(simd a, simd b) noexcept
Ordered lane greater-than, with the native less-than operands reversed.
static constexpr std::size_t lanes
Number of logical FP16 lanes, with no padding lanes.
mask mask_type
Generic mask spelling for the full-register lane mask.
friend constexpr simd sqrt(simd a) noexcept
friend constexpr mask operator>=(simd a, simd b) noexcept
Ordered lane greater-or-equal, with native less-or-equal operands reversed.
simd< T, 8, architecture > rebind
static constexpr simd loadu(fp16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
static constexpr simd load_memory(fp16 const *p) noexcept
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a FP16 representation without changing its bits.
constexpr void store(fp16 *p) const noexcept
Store 8 FP16 objects with the default alignment contract of store_memory().
friend constexpr simd operator/(simd a, simd b) noexcept
friend constexpr mask operator==(simd a, simd b) noexcept
simd< mask16, 8, architecture > mask
Full-register predicate with zero or all-one bits in each 16-bit lane.
friend constexpr simd select(mask m, simd a, simd b) noexcept
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
float16x8_t native_type
Native 128-bit FP16 register representation; native bridges copy bits.
friend constexpr mask operator!=(simd a, simd b) noexcept
Lane inequality, true for unordered NaN operands; complements native equality.
friend constexpr mask operator<=(simd a, simd b) noexcept
Ordered lane less-or-equal. NaNs compare false; native FPCR/FPSR semantics apply.
mask predicate_type
Explicit predicate spelling for the same full-register mask type.
static constexpr simd from_native(native_type value) noexcept
Copy a native FP16 register into this vector, preserving every representation bit.
friend constexpr simd operator-(simd a) noexcept
Apply native FNEG to each half lane; no scalar half-to-float conversion occurs.
static constexpr simd load_partial(fp16 const *p, std::size_t n, fp16 fill=fp16::from_bits(0)) noexcept
static constexpr simd load(fp16 const *p) noexcept
Load 8 FP16 objects with the default alignment contract of load_memory().
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
fp16 value_type
Scalar storage element; each lane retains all 16 representation bits.
friend constexpr simd operator*(simd a, simd b) noexcept
Multiply corresponding half lanes, rounding directly under the caller's FPCR.
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store_memory(fp16 *p) const noexcept
constexpr void store_bits(std::uint16_t *p) const noexcept
friend constexpr simd operator+(simd a, simd b) noexcept
Add corresponding half lanes, rounding directly under the caller's FPCR.
constexpr void store_partial(fp16 *p, std::size_t n) const noexcept
simd< mask16, 8, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
friend constexpr simd operator-(simd a, simd b) noexcept
Subtract corresponding half lanes, rounding directly under the caller's FPCR.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr simd(std::array< fp16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
simd< std::uint16_t, 8, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
friend constexpr mask operator<(simd a, simd b) noexcept
Ordered lane less-than. NaNs compare false; native FPCR/FPSR semantics apply.
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
static constexpr isa architecture
The distinct compile-time NEON_FP16 instruction profile.
constexpr void storeu(fp16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
simd register_type
This one-register vector type, for generic register-based algorithms.
friend constexpr mask operator>(simd a, simd b) noexcept
Ordered lane greater-than, with the native less-than operands reversed.
Omitted architecture arguments use the native.simd provider's baseline.